/*++

Copyright (c) 2002-2008 Microsoft Corporation

Module Name:

DISPVID.C

Abstract:

This source file contains routines which update the edit control
to display information about USB Video descriptors.

Environment:

user mode

Revision History:

11-22-2002 : created
03-28-2003 : major revisions from latest specs.
03-28-2008 : include USB Video Class 1.1

--*/

//*****************************************************************************
// I N C L U D E S
//*****************************************************************************

#include "uvcview.h"
#include "h264.h"

//*****************************************************************************
// G L O B A L S    P R I V A T E    T O    T H I S    F I L E
//*****************************************************************************

int StillMethod = 0;

//
// USB Device Class Definition for Video Devices 0.8b version
//
// 3.6.2.3  Camera Terminal Descriptor
//
STRINGLIST slCameraControl1[] =
{
	{1,         "Scanning Mode",            ""},
	{2,         "Auto-Exposure Mode",       ""},
	{4,         "Auto-Exposure Priority",   ""},
	{8,         "Exposure Time (Absolute)", ""},
	{0x10,      "Exposure Time (Relative)", ""},
	{0x20,      "Focus (Absolute)",         ""},
	{0x40,      "Focus (Relative)",         ""},
	{0x80,      "Iris (Absolute)",          ""},
};
STRINGLIST slCameraControl2[] =
{
	{1,         "Iris (Relative)",          ""},
	{2,         "Zoom (Absolute)",          ""},
	{4,         "Zoom (Relative)",          ""},
	{8,         "PanTilt (Absolute)",       ""},
	{0x10,      "PanTilt (Relative)",       ""},
	{0x20,      "Roll (Absolute)",          ""},
	{0x40,      "Roll (Relative)",          ""},
	{0x80,      "Reserved",                 ""},
};
STRINGLIST slCameraControl3[] =
{
	{1,         "Reserved",                 ""},
	{2,         "Focus, Auto",              ""},
	{4,         "Privacy",                  ""},
	{8,         "Focus, Simple",            ""},
	{0x10,      "Window",                   ""},
	{0x20,      "Region of Interest",       ""},
	{0x40,      "Reserved",                 ""},
	{0x80,      "Reserved",                 ""},
};

// 3.6.2.5  Processing Unit Descriptor
//
STRINGLIST slProcessorControls1[] =
{
	{1,         "Brightness",                ""},
	{2,         "Contrast",                  ""},
	{4,         "Hue",                       ""},
	{8,         "Saturation",                ""},
	{0x10,      "Sharpness",                 ""},
	{0x20,      "Gamma",                     ""},
	{0x40,      "White Balance Temperature", ""},
	{0x80,      "White Balance Component",   ""},
};
STRINGLIST slProcessorControls2[] =
{
	{1,         "Backlight Compensation",          ""},
	{2,         "Gain",                            ""},
	{4,         "Power Line Frequency",            ""},
	{8,         "Hue, Auto",                       ""},
	{0x10,      "White Balance Temperature, Auto", ""},
	{0x20,      "White Balance Component, Auto",   ""},
	{0x40,      "Digital Multiplier",              ""},
	{0x80,      "Digital Multiplier Limit",        ""},
};
STRINGLIST slProcessorControls3[] =
{
	{1,         "Analog Video Standard",           ""},
	{2,         "Analog Video Lock Status",        ""},
	{4,         "Contrast, Auto",                  ""},
	{8,         "Reserved",                        ""},
	{0x10,      "Reserved",                        ""},
	{0x20,      "Reserved",                        ""},
	{0x40,      "Reserved",                        ""},
	{0x80,      "Reserved",                        ""},
};


STRINGLIST slProcessorVideoStandards[] =
{
	{1,         "None",                     ""},
	{2,         "NTSC  - 525/60",           ""},
	{4,         "PAL   - 625/50",           ""},
	{8,         "SECAM - 625/50",           ""},
	{0x10,      "NTSC  - 625/50",           ""},
	{0x20,      "PAL   - 525/60",           ""},
	{0x40,      "Reserved",                 ""},
	{0x80,      "Reserved",                 ""},
};

// 3.8.2.1  Input Header Descriptor
//
STRINGLIST slInputHeaderControls[] =
{
	{1,         "Key Frame Rate"         , ""},
	{2,         "P Frame Rate"           , ""},
	{4,         "Compression Quality"    , ""},
	{8,         "Compression Window Size", ""},
	{0x10,      "Generate Key Frame"     , ""},
	{0x20,      "Update Frame Segment"   , ""},
	{0x40,      "Reserved"               , ""},
	{0x80,      "Reserved"               , ""},
};

STRINGLIST slOutputHeaderControls[] =
{
	{1,         "Key Frame Rate"         , ""},
	{2,         "P Frame Rate"           , ""},
	{4,         "Compression Quality"    , ""},
	{8,         "Compression Window Size", ""},
	{0x10,      "Reserved"               , ""},
	{0x20,      "Reserved"               , ""},
	{0x40,      "Reserved"               , ""},
	{0x80,      "Reserved"               , ""},
};

STRINGLIST slMediaTransportControls[] =
{
	{1,         "Transport Control"            , ""},
	{2,         "Absolute Track Number Control", ""},
	{4,         "Media Information"            , ""},
	{8,         "Time Code Information"        , ""},
	{0x10,      "Reserved"                     , ""},
	{0x20,      "Reserved"                     , ""},
	{0x40,      "Reserved"                     , ""},
	{0x80,      "Reserved"                     , ""},
};

STRINGLIST slMediaTransportModes1[] =
{
	{1,         "Play Forward",         ""},
	{2,         "Pause",                ""},
	{4,         "Rewind",               ""},
	{8,         "Fast Forward",         ""},
	{0x10,      "High Speed Rewind",    ""},
	{0x20,      "Stop",                 ""},
	{0x40,      "Eject",                ""},
	{0x80,      "Play Next Frame",      ""},
};

STRINGLIST slMediaTransportModes2[] =
{
	{1,         "Play Slowest Forward", ""},
	{2,         "Play Slow Forward 4",  ""},
	{4,         "Play Slow Forward 3",  ""},
	{8,         "Play Slow Forward 2",  ""},
	{0x10,      "Play Slow Forward 1",  ""},
	{0x20,      "Play X1",              ""},
	{0x40,      "Play Fast Forward 1",  ""},
	{0x80,      "Play Fast Forward 2",  ""},
};

STRINGLIST slMediaTransportModes3[] =
{
	{1,         "Play Fast Forward 3",  ""},
	{2,         "Play Fast Forward 4",  ""},
	{4,         "Play Fastest Forward", ""},
	{8,         "Play Previous Frame",  ""},
	{0x10,      "Play Slowest Reverse", ""},
	{0x20,      "Play Slow Reverse 4",  ""},
	{0x40,      "Play Slow Reverse 3",  ""},
	{0x80,      "Play Slow Reverse 2",  ""},
};

STRINGLIST slMediaTransportModes4[] =
{
	{1,         "Play Slow Reverse 1",  ""},
	{2,         "Play X1 Reverse",      ""},
	{4,         "Play Fast Reverse 1",  ""},
	{8,         "Play Fast Reverse 2",  ""},
	{0x10,      "Play Fast Reverse 3",  ""},
	{0x20,      "Play Fast Reverse 4",  ""},
	{0x40,      "Play Fastest Reverse", ""},
	{0x80,      "Record StateStart",    ""},
};

STRINGLIST slMediaTransportModes5[] =
{
	{1,         "Record Pause",         ""},
	{2,         "Reserved",             ""},
	{4,         "Reserved",             ""},
	{8,         "Reserved",             ""},
	{0x10,      "Reserved",             ""},
	{0x20,      "Reserved",             ""},
	{0x40,      "Reserved",             ""},
	{0x80,      "Reserved",             ""},
};

STRINGLIST slInputTermTypes[] =
{
	{0x0100,    "TT_VENDOR_SPECIFIC",         "I//O"},
	{0x0101,    "TT_STREAMING",               "I//O"},
	{0x0400,    "EXTERNAL_VENDOR_SPECIFIC",   "I//O"},
	{0x0401,    "COMPOSITE_CONNECTOR",        "I//O"},
	{0x0402,    "SVIDEO_CONNECTOR",           "I//O"},
	{0x0403,    "COMPONENT_CONNECTOR",        "I//O"},
	{0x0200,    "ITT_VENDOR_SPECIFIC",        "I"},
	{0x0201,    "ITT_CAMERA",                 "I"},
	{0x0202,    "ITT_MEDIA_TRANSPORT_INPUT",  "I"},
};
STRINGLIST slOutputTermTypes[] =
{
	{0x0100,    "TT_VENDOR_SPECIFIC",         "I//O"},
	{0x0101,    "TT_STREAMING",               "I//O"},
	{0x0400,    "EXTERNAL_VENDOR_SPECIFIC",   "I//O"},
	{0x0401,    "COMPOSITE_CONNECTOR",        "I//O"},
	{0x0402,    "SVIDEO_CONNECTOR",           "I//O"},
	{0x0403,    "COMPONENT_CONNECTOR",        "I//O"},
	{0x0300,    "OTT_VENDOR_SPECIFIC",        "O"},
	{0x0301,    "OTT_DISPLAY",                "O"},
	{0x0302,    "OTT_MEDIA_TRANSPORT_OUTPUT", "O"},
};

//*****************************************************************************
// L O C A L    F U N C T I O N    P R O T O T Y P E S
//*****************************************************************************

BOOL
DisplayVCHeader(
	PVIDEO_CONTROL_HEADER_UNIT VCInterfaceDesc
);
BOOL
DisplayVCInputTerminal(
	PVIDEO_INPUT_TERMINAL   VidITDesc,
	PSTRING_DESCRIPTOR_NODE StringDescs,
	DEVICE_POWER_STATE      LatestDevicePowerState
);

BOOL
DisplayVCOutputTerminal(
	PVIDEO_OUTPUT_TERMINAL  VidOTDesc,
	PSTRING_DESCRIPTOR_NODE StringDescs,
	DEVICE_POWER_STATE      LatestDevicePowerState
);

BOOL
DisplayVCCameraTerminal(
	PVIDEO_CAMERA_TERMINAL CameraDesc
);
BOOL
DisplayVCMediaTransInputTerminal(
	PVIDEO_INPUT_MTT VCMedTransInDesc
);
BOOL
DisplayVCMediaTransOutputTerminal(
	PVIDEO_OUTPUT_MTT VCMedTransOutDesc
);
BOOL
DisplayVCSelectorUnit(
	PVIDEO_SELECTOR_UNIT    VidSelectorDesc,
	PSTRING_DESCRIPTOR_NODE StringDescs,
	DEVICE_POWER_STATE      LatestDevicePowerState
);

BOOL
DisplayVCProcessingUnit(
	PVIDEO_PROCESSING_UNIT  VidProcessingDesc,
	PSTRING_DESCRIPTOR_NODE StringDescs,
	DEVICE_POWER_STATE      LatestDevicePowerState
);

BOOL
DisplayVCExtensionUnit(
	PVIDEO_EXTENSION_UNIT   VidExtensionDesc,
	PSTRING_DESCRIPTOR_NODE StringDescs,
	DEVICE_POWER_STATE      LatestDevicePowerState
);

BOOL
DisplayVidInHeader(
	PVIDEO_STREAMING_INPUT_HEADER VidInHeaderDesc
);
BOOL
DisplayVidOutHeader(
	PVIDEO_STREAMING_OUTPUT_HEADER VidOutHeaderDesc
);
BOOL
DisplayStillImageFrame(
	PVIDEO_STILL_IMAGE_FRAME StillFrameDesc
);
BOOL
DisplayColorMatching(
	PVIDEO_COLORFORMAT ColorMatchDesc
);
BOOL
DisplayUncompressedFormat(
	PVIDEO_FORMAT_UNCOMPRESSED UnCompFormatDesc
);
BOOL
DisplayUncompressedFrameType(
	PVIDEO_FRAME_UNCOMPRESSED UnCompFrameDesc
);
BOOL
DisplayUnComContinuousFrameType(
	PVIDEO_FRAME_UNCOMPRESSED UContinuousDesc
);
BOOL
DisplayUnComDiscreteFrameType(
	PVIDEO_FRAME_UNCOMPRESSED UDiscreteDesc
);
BOOL
DisplayMJPEGFormat(
	PVIDEO_FORMAT_MJPEG MJPEGFormatDesc
);
BOOL
DisplayMJPEGFrameType(
	PVIDEO_FRAME_MJPEG MJPEGFrameDesc
);
BOOL
DisplayMJPEGContinuousFrameType(
	PVIDEO_FRAME_MJPEG MContinuousDesc
);
BOOL
DisplayMJPEGDiscreteFrameType(
	PVIDEO_FRAME_MJPEG MDiscreteDesc
);
BOOL
DisplayMPEG1SSFormat(
	PVIDEO_FORMAT_MPEG1SS MPEG1SSFormatDesc
);
BOOL
DisplayMPEG2PSFormat(
	PVIDEO_FORMAT_MPEG2PS MPEG2PSFormatDesc
);
BOOL
DisplayMPEG2TSFormat(
	PVIDEO_FORMAT_MPEG2TS MPEG2TSFormatDesc
);
BOOL
DisplayMPEG4SLFormat(
	PVIDEO_FORMAT_MPEG4SL MPEG4SLFormatDesc
);
BOOL
DisplayDVFormat(
	PVIDEO_FORMAT_DV DVFormatDesc
);
BOOL
DisplayVendorVidFormat(
	PVIDEO_FORMAT_VENDOR VendorVidFormatDesc
);
BOOL
DisplayVendorVidFrameType(
	PVIDEO_FRAME_VENDOR VendorVidFrameDesc
);
BOOL
DisplayVendorVidContinuousFrameType(
	PVIDEO_FRAME_VENDOR VContinuousDesc
);
BOOL
DisplayVendorVidDiscreteFrameType(
	PVIDEO_FRAME_VENDOR VDiscreteDesc
);
BOOL
DisplayFramePayloadFormat(
	PVIDEO_FORMAT_FRAME FramePayloadFormatDesc
);
BOOL
DisplayFramePayloadFrame(
	PVIDEO_FRAME_FRAME FramePayloadFrameDesc
);
BOOL
DisplayFramePayloadContinuousFrameType(
	PVIDEO_FRAME_FRAME FContinuousDesc
);
BOOL
DisplayFramePayloadDiscreteFrameType(
	PVIDEO_FRAME_FRAME FDiscreteDesc
);
BOOL
DisplayStreamPayload(
	PVIDEO_FORMAT_STREAM StreamPayloadDesc
);
BOOL
DisplayVSEndpoint(
	PVIDEO_CS_INTERRUPT VidEndpointDesc
);
VOID
VDisplayBytes(
	PUCHAR Data,
	USHORT Len
);
PCHAR
VidFormatGUIDCodeToName(
	REFGUID VidFormatGUIDCode
);
UINT
GetVCInterfaceSize(
	PVIDEO_CONTROL_HEADER_UNIT VCInterfaceDesc
);
UINT
CheckForColorMatchingDesc(
	PVIDEO_SPECIFIC FormatDesc,
	UCHAR bNumFrameDescriptors,
	UCHAR bDescriptorSubtype
);
UINT
GetVSInterfaceSize(
	PUSB_COMMON_DESCRIPTOR VidInHeaderDesc,
	USHORT wTotalLength
);
BOOL
ValidateTerminalID(
	UINT uTerminalID
);
VOID
VDisplayDescString(
	UINT uControlSize,
	PUCHAR pControl,
	PSTRINGLIST pslControl
);

//*****************************************************************************
// L O C A L    F U N C T I O N S
//*****************************************************************************

//*****************************************************************************
//
// DisplayVideoDescriptor() UPDATED
//
// VidCommonDesc - An Video Class Descriptor
//
// bInterfaceSubClass - The SubClass of the Interface containing the descriptor
//
//*****************************************************************************

BOOL
DisplayVideoDescriptor(
	PVIDEO_SPECIFIC VidCommonDesc,
	UCHAR                        bInterfaceSubClass,
	PSTRING_DESCRIPTOR_NODE      StringDescs,
	DEVICE_POWER_STATE           LatestDevicePowerState
)
{
	//@@DisplayVideoDescriptor -Class-Specific Video Descriptor
	switch (VidCommonDesc->bDescriptorType)
	{
	case CS_INTERFACE:
		//@@DisplayVideoDescriptor -Class-Specific Video Interface Descriptor
		switch (bInterfaceSubClass)
		{
		case VIDEO_SUBCLASS_CONTROL:
			//@@DisplayVideoDescriptor -Class-Specific Video Control Interface Descriptor
			switch (VidCommonDesc->bDescriptorSubtype)
			{
			case VC_HEADER:
				return DisplayVCHeader(
					(PVIDEO_CONTROL_HEADER_UNIT)VidCommonDesc);

			case INPUT_TERMINAL:
				return DisplayVCInputTerminal(
					(PVIDEO_INPUT_TERMINAL)VidCommonDesc,
					StringDescs,
					LatestDevicePowerState);

			case OUTPUT_TERMINAL:
				return DisplayVCOutputTerminal(
					(PVIDEO_OUTPUT_TERMINAL)VidCommonDesc,
					StringDescs,
					LatestDevicePowerState);

			case SELECTOR_UNIT:
				return DisplayVCSelectorUnit(
					(PVIDEO_SELECTOR_UNIT)VidCommonDesc,
					StringDescs,
					LatestDevicePowerState);

			case PROCESSING_UNIT:
				return DisplayVCProcessingUnit(
					(PVIDEO_PROCESSING_UNIT)VidCommonDesc,
					StringDescs,
					LatestDevicePowerState);

			case EXTENSION_UNIT:
				return DisplayVCExtensionUnit(
					(PVIDEO_EXTENSION_UNIT)VidCommonDesc,
					StringDescs,
					LatestDevicePowerState);

#ifdef H264_SUPPORT
			case H264_ENCODING_UNIT:
				return DisplayVCH264EncodingUnit(
					(PVIDEO_ENCODING_UNIT)VidCommonDesc
				);

#endif

#ifdef H264_SUPPORT
			case MAX_TYPE_UNIT + 1:
				// for H.264, the bDescriptorSubtype = 7, which is equal to MAX_TYPE_UNIT
				// so now MAX_TYPE_UNIT needs to be set to 8 
				//(TODO: need to change nt\sdpublic\internal\drivers\inc\uvcdesc.h's define
				// of MAX_TYPE_UNIT from7 to 8, and ad the type for H.264 = 8)
#else
			case MAX_TYPE_UNIT:
#endif
				//@@TestCase B1.1
				//@@CAUTION
				//@@Descriptor Field - bDescriptorSubtype
				//@@An undefined descriptor subtype has been defined
				AppendTextBuffer("*!*CAUTION:  This is an undefined class specific "\
					"Video Control bDescriptorSubtype\r\n");
				break;

			default:
				//@@TestCase B1.2
				//@@ERROR
				//@@Descriptor Field - bDescriptorSubtype
				//@@An unknown descriptor subtype has been defined
				AppendTextBuffer("*!*ERROR:  unknown bDescriptorSubtype\r\n");
				OOPS();
				break;
			}
			break;

		case VIDEO_SUBCLASS_STREAMING:
			//@@DisplayVideoDescriptor -Class-Specific Video Streaming Interface Descriptor
			switch (VidCommonDesc->bDescriptorSubtype)
			{
			case VS_INPUT_HEADER:
				return DisplayVidInHeader(
					(PVIDEO_STREAMING_INPUT_HEADER)VidCommonDesc);

			case VS_OUTPUT_HEADER:
				return DisplayVidOutHeader(
					(PVIDEO_STREAMING_OUTPUT_HEADER)VidCommonDesc);

			case VS_STILL_IMAGE_FRAME:
				return DisplayStillImageFrame(
					(PVIDEO_STILL_IMAGE_FRAME)VidCommonDesc);

			case VS_FORMAT_UNCOMPRESSED:
#ifdef H264_SUPPORT
			{
				BOOL retCode = DisplayUncompressedFormat((PVIDEO_FORMAT_UNCOMPRESSED)VidCommonDesc);
				g_expectedNumberOfUncompressedFrameFrameDescriptors += ((PVIDEO_FORMAT_UNCOMPRESSED)VidCommonDesc)->bNumFrameDescriptors;
				return retCode;
			}
#else
				return DisplayUncompressedFormat(
					(PVIDEO_FORMAT_UNCOMPRESSED)VidCommonDesc);
#endif

			case VS_FRAME_UNCOMPRESSED:
#ifdef H264_SUPPORT
			{
				BOOL retCode = DisplayUncompressedFrameType((PVIDEO_FRAME_UNCOMPRESSED)VidCommonDesc);
				g_numberOfUncompressedFrameFrameDescriptors++;
				return retCode;
			}
#else
				return DisplayUncompressedFrameType(
					(PVIDEO_FRAME_UNCOMPRESSED)VidCommonDesc);
#endif

#ifdef H264_SUPPORT
			case VS_FORMAT_H264:
			{
				BOOL retCode = DisplayVCH264Format((PVIDEO_FORMAT_H264)VidCommonDesc);
				g_expectedNumberOfH264FrameDescriptors += ((PVIDEO_FORMAT_H264)VidCommonDesc)->bNumFrameDescriptors;
				return retCode;
			}

			case VS_FRAME_H264:
			{
				BOOL  retCode = DisplayVCH264FrameType((PVIDEO_FRAME_H264)VidCommonDesc);
				g_numberOfH264FrameDescriptors++;
				return retCode;
			}
#endif

			case VS_FORMAT_MJPEG:
#ifdef H264_SUPPORT // additional checks
			{
				BOOL retCode = DisplayMJPEGFormat((PVIDEO_FORMAT_MJPEG)VidCommonDesc);
				g_expectedNumberOfMJPEGFrameDescriptors += ((PVIDEO_FORMAT_MJPEG)VidCommonDesc)->bNumFrameDescriptors;
				return retCode;
			}
#else
				return DisplayMJPEGFormat(
					(PVIDEO_FORMAT_MJPEG)VidCommonDesc);
#endif

			case VS_FRAME_MJPEG:
#ifdef H264_SUPPORT
			{
				BOOL  retCode = DisplayMJPEGFrameType((PVIDEO_FRAME_MJPEG)VidCommonDesc);
				g_numberOfMJPEGFrameDescriptors++;
				return retCode;
			}

#else
				return DisplayMJPEGFrameType(
					(PVIDEO_FRAME_MJPEG)VidCommonDesc);
#endif



			case VS_FORMAT_MPEG1:
			{
				if (UVC10 == g_chUVCversion)
				{
					return DisplayMPEG1SSFormat(
						(PVIDEO_FORMAT_MPEG1SS)VidCommonDesc);
				}
				else // this format is obsoleted in UVC version >= 1.1
				{
					AppendTextBuffer("*!*ERROR:  obsoleted bDescriptorSubtype\r\n");
					OOPS();
					break;
				}
			}

			case VS_FORMAT_MPEG2PS:
			{
				if (UVC10 == g_chUVCversion)
				{
					return DisplayMPEG2PSFormat(
						(PVIDEO_FORMAT_MPEG2PS)VidCommonDesc);
				}
				else // this format is obsoleted in UVC version >= 1.1
				{
					AppendTextBuffer("*!*ERROR:  obsoleted bDescriptorSubtype\r\n");
					OOPS();
					break;
				}
			}

			case VS_FORMAT_MPEG2TS:
				return DisplayMPEG2TSFormat(
					(PVIDEO_FORMAT_MPEG2TS)VidCommonDesc);

			case VS_FORMAT_MPEG4SL:
			{
				if (UVC10 == g_chUVCversion)
				{
					return DisplayMPEG4SLFormat(
						(PVIDEO_FORMAT_MPEG4SL)VidCommonDesc);
				}
				else // this format is obsoleted in UVC version >= 1.1
				{
					AppendTextBuffer("*!*ERROR:  obsoleted bDescriptorSubtype\r\n");
					OOPS();
					break;
				}
			}

			case VS_FORMAT_DV:
				return DisplayDVFormat(
					(PVIDEO_FORMAT_DV)VidCommonDesc);

			case VS_COLORFORMAT:
				return DisplayColorMatching(
					(PVIDEO_COLORFORMAT)VidCommonDesc);

			case VS_FORMAT_VENDOR:
			{
				if (UVC10 == g_chUVCversion)
				{
					return DisplayVendorVidFormat(
						(PVIDEO_FORMAT_VENDOR)VidCommonDesc);
				}
				else // this format is obsoleted in UVC version >= 1.1
				{
					AppendTextBuffer("*!*ERROR:  obsoleted bDescriptorSubtype\r\n");
					OOPS();
					break;
				}
			}

			case VS_FRAME_VENDOR:
			{
				if (UVC10 == g_chUVCversion)
				{
					return DisplayVendorVidFrameType(
						(PVIDEO_FRAME_VENDOR)VidCommonDesc);
				}
				else // this format is obsoleted in UVC version >= 1.1
				{
					AppendTextBuffer("*!*ERROR:  obsoleted bDescriptorSubtype\r\n");
					OOPS();
					break;
				}
			}

			case VS_FORMAT_FRAME_BASED:
			{
				if (UVC10 != g_chUVCversion)
				{
					return DisplayFramePayloadFormat(
						(PVIDEO_FORMAT_FRAME)VidCommonDesc);
				}
				else // this format did not exist in UVC 1.0
				{
					AppendTextBuffer("*!*ERROR: bDescriptorSubtype did not exist in UVC 1.0\r\n");
					OOPS();
					break;
				}
			}

			case VS_FRAME_FRAME_BASED:
			{
				if (UVC10 != g_chUVCversion)
				{
					return DisplayFramePayloadFrame(
						(PVIDEO_FRAME_FRAME)VidCommonDesc);
				}
				else // this format did not exist in UVC 1.0
				{
					AppendTextBuffer("*!*ERROR: bDescriptorSubtype did not exist in UVC 1.0\r\n");
					OOPS();
					break;
				}
			}

			case VS_FORMAT_STREAM_BASED:
			{
				if (UVC10 != g_chUVCversion)
				{
					return DisplayStreamPayload(
						(PVIDEO_FORMAT_STREAM)VidCommonDesc);
				}
				else // this format did not exist in UVC 1.0
				{
					AppendTextBuffer("*!*ERROR: bDescriptorSubtype did not exist in UVC 1.0\r\n");
					OOPS();
					break;
				}
			}

			case VS_DESCRIPTOR_UNDEFINED:
				//@@TestCase B1.3
				//@@CAUTION
				//@@Descriptor Field - bDescriptorSubtype
				//@@An undefined descriptor subtype has been defined
				AppendTextBuffer("*!*CAUTION:  This is an undefined class specific Video "\
					"Streaming bDescriptorSubtype\r\n");
				break;

			default:
				//@@TestCase B1.4
				//@@ERROR
				//@@Descriptor Field - bDescriptorSubtype
				//@@An unknown descriptor subtype has been defined
				AppendTextBuffer("*!*ERROR:  unknown bDescriptorSubtype\r\n");
				OOPS();
				break;
			}
			break;

		default:
			//@@TestCase B1.6
			//@@ERROR
			//@@Descriptor Field - bInterfaceSubClass
			//@@An unknown interface sub-class has been defined
			AppendTextBuffer("*!*ERROR:  unknown bInterfaceSubClass\r\n");
			OOPS();
			break;
		}
		break;

	case CS_ENDPOINT:
		//@@DisplayVideoDescriptor -Class-Specific Video Endpoint Descriptor
		switch (VidCommonDesc->bDescriptorSubtype)
		{
			//@@TestCase B1.7
			//@@CAUTION
			//@@Descriptor Field - bInterfaceSubtype
			//@@An undefined descriptor subtype has been defined
		case EP_UNDEFINED:
			AppendTextBuffer("*!*CAUTION:  This is an undefined bDescriptorSubtype\r\n");
			break;
			//@@TestCase B1.8
			//@@Not yet implemented - Priority 3
			//@@Descriptor Field - bDescriptorSubtype
			//@@Question:  How valid are VIDEO_EP_GENERAL and VIDEO_EP_ENDPOINT?  Should we test?
		case EP_GENERAL:
			break;
		case EP_ENDPOINT:
			break;
		case EP_INTERRUPT:
			return DisplayVSEndpoint(
				(PVIDEO_CS_INTERRUPT)VidCommonDesc);
			break;
		default:
			//@@TestCase B1.9
			//@@ERROR
			//@@Descriptor Field - bDescriptorSubtype
			//@@An unknown descriptor subtype has been defined
			AppendTextBuffer("*!*CAUTION:  Unknown bDescriptorSubtype");
			break;
		}
		break;
		//@@DisplayVideoDescriptor -Class-Specific Video Device Descriptor
		//@@DisplayVideoDescriptor -Class-Specific Video Configuration Descriptor
		//@@DisplayVideoDescriptor -Class-Specific Video String Descriptor
		//@@DisplayVideoDescriptor -Class-Specific Video Undefined Descriptor
		//@@TestCase B1.10
		//@@Not yet implemented - Priority 3
		//@@Descriptor -Class-Specific Device, Configuration, String, Undefined
		//@@Descriptor Field - bDescriptorType
		//@@Question:  How valid are these Descriptor Types?  Should we test?

		/*        case USB_VIDEO_CS_DEVICE:
		AppendTextBuffer("USB_VIDEO_CS_DEVICE bDescriptorType\r\n");
		break;

		case USB_VIDEO_CS_CONFIGURATION:
		AppendTextBuffer("USB_VIDEO_CS_CONFIGURATION bDescriptorType\r\n");
		break;

		case USB_VIDEO_CS_STRING:
		AppendTextBuffer("USB_VIDEO_CS_STRING bDescriptorType\r\n");
		break;

		case USB_VIDEO_CS_UNDEFINED:
		AppendTextBuffer("USB_VIDEO_CS_UNDEFINED bDescriptorType\r\n");
		break;
		*/
	default:
		//@@TestCase B1.11
		//@@ERROR
		//@@Descriptor Field - bDescriptorType
		//@@An unknown descriptor type has been defined
		AppendTextBuffer("*!*CAUTION:  Unknown bDescriptorSubtype");
		OOPS();
		break;
	}

	return FALSE;
}


//*****************************************************************************
//
// DisplayVCHeader()
//
//*****************************************************************************

BOOL
DisplayVCHeader(
	PVIDEO_CONTROL_HEADER_UNIT VCInterfaceDesc
)
{
	//@@DisplayVCHeader -Video Control Interface Header
	UINT   i = 0;
	UINT   uSize = 0;
	PUCHAR pData = NULL;

	AppendTextBuffer("\r\n          ===>Class-Specific Video Control Interface Header "\
		"Descriptor<===\r\n");
	AppendTextBuffer("bLength:                           0x%02X\r\n", VCInterfaceDesc->bLength);
	AppendTextBuffer("bDescriptorType:                   0x%02X\r\n", VCInterfaceDesc->bDescriptorType);
	AppendTextBuffer("bDescriptorSubtype:                0x%02X\r\n", VCInterfaceDesc->bDescriptorSubtype);
	if (UVC10 == g_chUVCversion)
	{
		AppendTextBuffer("bcdVDC:                          0x%04X\r\n", VCInterfaceDesc->bcdVideoSpec);
	}
	else
	{
		AppendTextBuffer("bcdUVC:                          0x%04X\r\n", VCInterfaceDesc->bcdVideoSpec);
	}
	AppendTextBuffer("wTotalLength:                    0x%04X", VCInterfaceDesc->wTotalLength);

	// Verify the total interface size (size of this header and all descriptors 
	//   following until and not including the first endpoint)
	uSize = GetVCInterfaceSize(VCInterfaceDesc);
	if (uSize != VCInterfaceDesc->wTotalLength) {
		AppendTextBuffer("\r\n*!*ERROR: Invalid total interface size 0x%02X, should be 0x%02X\r\n",
			VCInterfaceDesc->wTotalLength, uSize);
	}
	else {
		AppendTextBuffer("  -> Validated\r\n");
	}
	AppendTextBuffer("dwClockFreq:                 0x%08X",
		VCInterfaceDesc->dwClockFreq);
	if (gDoAnnotation)
	{
		AppendTextBuffer(" = (%d) Hz", VCInterfaceDesc->dwClockFreq);
	}
	AppendTextBuffer("\r\nbInCollection:                     0x%02X\r\n",
		VCInterfaceDesc->bInCollection);

	// baInterfaceNr is a variable length field
	// Size is in bInCollection
	for (i = 1, pData = (PUCHAR)&VCInterfaceDesc->bInCollection;
		i <= VCInterfaceDesc->bInCollection; i++, pData++)
	{
		AppendTextBuffer("baInterfaceNr[%d]:                  0x%02X\r\n",
			i, *pData);
	}

	uSize = (sizeof(VIDEO_CONTROL_HEADER_UNIT) + VCInterfaceDesc->bInCollection);
	if (VCInterfaceDesc->bLength != uSize)
	{
		//@@TestCase B2.1 (also in Descript.c)
		//@@ERROR
		//@@Descriptor Field - bLength
		//@@The declared length in the device descriptor is less than required length in 
		//@@  the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  bLength of %d incorrect, should be %d\r\n",
			VCInterfaceDesc->bLength, uSize);
		OOPS();
	}

	//@@TestCase B2.2 (also in Descript.c)
	//@@WARNING
	//@@Descriptor Field - bcdVDC
	//@@The bcdVDC version of the device is not the same as the version of used by USBView
	if (VCInterfaceDesc->bcdVideoSpec < BCDVDC)
	{
		AppendTextBuffer("*!*WARNING: This device is set to the old USB Video "\
			"Class spec version 0x%04X\r\n", VCInterfaceDesc->bcdVideoSpec);
		OOPS();
	}

	if (VCInterfaceDesc->dwClockFreq < 1)
	{
		//@@TestCase B2.3 (Descript.c Line 70)
		//@@WARNING
		//@@dwClockFrequency should be greater than 0
		//@@Question should we check that any non-zero value is accurate
		AppendTextBuffer("*!*ERROR:  dwClockFreq must be non-zero\r\n");
		OOPS();
	}

	//@@TestCase B2.4
	//@@Not yet implemented - Priority 1
	//@@Descriptor Field - baInterfaceNr
	//@@We should test to verify each interface number is valid?
	//    for (i=0; i<VCInterfaceDesc->bInCollection; i++) 
	//      {AppendTextBuffer("baInterfaceNr[%d]:                  0x%02X\r\n", i+1, 
	//        VCInterfaceDesc->baInterfaceNr[i]);}


	if (gDoAnnotation)
	{
		switch (g_chUVCversion)
		{
		case UVC10:
			AppendTextBuffer("USB Video Class device: spec version 1.0\r\n");
			break;
		case UVC11:
			AppendTextBuffer("USB Video Class device: spec version 1.1\r\n");
			break;
#ifdef H264_SUPPORT
		case UVC15:
			AppendTextBuffer("USB Video Class device: spec version 1.5\r\n");
			break;
#endif

		default:
			break;
		}
	}
	return TRUE;
}


//*****************************************************************************
//
// DisplayVCInputTerminal()
//
//*****************************************************************************

BOOL
DisplayVCInputTerminal(
	PVIDEO_INPUT_TERMINAL   VidITDesc,
	PSTRING_DESCRIPTOR_NODE StringDescs,
	DEVICE_POWER_STATE      LatestDevicePowerState
)
{
	//@@DisplayVCInputTerminal -Video Control Input Terminal
	PCHAR pStr = NULL;

	AppendTextBuffer("\r\n          ===>Video Control Input Terminal Descriptor<===\r\n");

	AppendTextBuffer("bLength:                           0x%02X\r\n", VidITDesc->bLength);
	AppendTextBuffer("bDescriptorType:                   0x%02X\r\n", VidITDesc->bDescriptorType);
	AppendTextBuffer("bDescriptorSubtype:                0x%02X\r\n", VidITDesc->bDescriptorSubtype);
	AppendTextBuffer("bTerminalID:                       0x%02X\r\n", VidITDesc->bTerminalID);
	AppendTextBuffer("wTerminalType:                   0x%04X", VidITDesc->wTerminalType);
	if (gDoAnnotation)
	{
		pStr = GetStringFromList(slInputTermTypes,
			sizeof(slInputTermTypes) / sizeof(STRINGLIST),
			VidITDesc->wTerminalType,
			"Invalid Input Terminal Type");
		AppendTextBuffer(" = (%s)", pStr);
	}
	AppendTextBuffer("\r\n");

	AppendTextBuffer("bAssocTerminal:                    0x%02X\r\n", VidITDesc->bAssocTerminal);
	AppendTextBuffer("iTerminal:                         0x%02X\r\n", VidITDesc->iTerminal);
	if (gDoAnnotation)
	{
		if (VidITDesc->iTerminal)
		{
			// if executing this code, the configuration descriptor has been 
			// obtained.  If a device is suspended, then its configuration
			// descriptor was not obtained and we do not want errors to be 
			// displayed when string descriptors were not obtained.
			DisplayStringDescriptor(VidITDesc->iTerminal, StringDescs, LatestDevicePowerState);
		}
	}

	if (VidITDesc->bLength < sizeof(VIDEO_INPUT_TERMINAL))
	{
		//@@TestCase B3.1  (also in Descript.c)
		//@@ERROR
		//@@Descriptor Field - bLength
		//@@The declared length in the device descriptor is less than required length in 
		//@@  the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  bLength of %d is too small\r\n", VidITDesc->bLength);
		OOPS();
	}

	if (VidITDesc->bTerminalID < 1)
	{
		//@@TestCase B3.2 (descript.c  line 133)
		//@@ERROR
		//@@Descriptor Field - bTerminalID
		//@@bTerminalID should be greater than 0
		//@@Question: Should test to verify terminal number is valid
		AppendTextBuffer("*!*ERROR:  bTerminalID of %d is too small\r\n", VidITDesc->bTerminalID);
		OOPS();
	}

	if (!(pStr))
	{
		//@@TestCase B3.3
		//@@CAUTION
		//@@Descriptor Field - wTerminalType
		//@@No valid Terminal Type was found
		AppendTextBuffer("*!*CAUTION:  0x%04X is an unknown wTerminalType for an Input "\
			"Terminal\r\n", VidITDesc->wTerminalType);
		OOPS();
	}

	//@@TestCase B3.4
	//@@Not yet implemented - Priority 1
	//@@Descriptor Field - bAssocTerminal
	//@@Should test to verify terminal number is valid?
	//    AppendTextBuffer("bAssocTerminal:                    0x%02X\r\n", VidITDesc->bAssocTerminal);

	switch (VidITDesc->wTerminalType)
	{
	case 0x0100:  // TT_VENDOR_SPECIFIC Terminal Type
		break;
	case 0x0101:  // TT_STREAMING Terminal Type
		break;
	case 0x0200:  // ITT_VENDOR_SPECIFIC Terminal Type
		break;
	case 0x0201:  // ITT_CAMERA Terminal Type
		return DisplayVCCameraTerminal(
			(PVIDEO_CAMERA_TERMINAL)VidITDesc);
	case 0x0202:  // ITT_MEDIA_TRANSPORT_INPUT Terminal Type
		return DisplayVCMediaTransInputTerminal(
			(PVIDEO_INPUT_MTT)VidITDesc);
	case 0x0400:  // EXTERNAL_VENDOR_SPECIFIC Terminal Type
		break;
	case 0x0401:  // COMPOSITE_CONNECTOR Terminal Type
		break;
	case 0x0402:  // SVIDEO_CONNECTOR Terminal Type
		break;
	case 0x0403:  // COMPONENT_CONNECTOR Terminal Type
		break;
	default:
		break;
	}

	return TRUE;
}


//*****************************************************************************
//
// DisplayVCOutputTerminal()
//
//*****************************************************************************

BOOL
DisplayVCOutputTerminal(
	PVIDEO_OUTPUT_TERMINAL  VidOTDesc,
	PSTRING_DESCRIPTOR_NODE StringDescs,
	DEVICE_POWER_STATE      LatestDevicePowerState
)
{
	//@@DisplayVCOutputTerminal -Video Control Output Terminal
	PCHAR pStr = NULL;

	AppendTextBuffer("\r\n          ===>Video Control Output Terminal Descriptor<===\r\n");
	AppendTextBuffer("bLength:                           0x%02X\r\n", VidOTDesc->bLength);
	AppendTextBuffer("bDescriptorType:                   0x%02X\r\n", VidOTDesc->bDescriptorType);
	AppendTextBuffer("bDescriptorSubtype:                0x%02X\r\n", VidOTDesc->bDescriptorSubtype);
	AppendTextBuffer("bTerminalID:                       0x%02X\r\n", VidOTDesc->bTerminalID);
	AppendTextBuffer("wTerminalType:                   0x%04X", VidOTDesc->wTerminalType);
	if (gDoAnnotation)
	{
		pStr = GetStringFromList(slOutputTermTypes,
			sizeof(slOutputTermTypes) / sizeof(STRINGLIST),
			VidOTDesc->wTerminalType,
			"Invalid Output Terminal Type");
		AppendTextBuffer(" = (%s)", pStr);
	}
	AppendTextBuffer("\r\n");
	AppendTextBuffer("bAssocTerminal:                    0x%02X\r\n", VidOTDesc->bAssocTerminal);
	AppendTextBuffer("bSourceID:                         0x%02X\r\n", VidOTDesc->bSourceID);
	AppendTextBuffer("iTerminal:                         0x%02X\r\n", VidOTDesc->iTerminal);
	if (gDoAnnotation)
	{
		if (VidOTDesc->iTerminal)
		{
			// if executing this code, the configuration descriptor has been 
			// obtained.  If a device is suspended, then its configuration
			// descriptor was not obtained and we do not want errors to be 
			// displayed when string descriptors were not obtained.
			DisplayStringDescriptor(VidOTDesc->iTerminal, StringDescs, LatestDevicePowerState);
		}
	}

	if (VidOTDesc->bLength < sizeof(PVIDEO_OUTPUT_TERMINAL))
	{
		//@@TestCase B4.1  (also in Descript.c)
		//@@ERROR
		//@@Descriptor Field - bLength
		//@@The declared length in the device descriptor is less than required length in 
		//@@  the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  bLength of %d is too small\r\n", VidOTDesc->bLength);
		OOPS();
	}

	if (VidOTDesc->bTerminalID < 1)
	{
		//@@TestCase B4.2  (see Descript.c  line 328)
		//@@ERROR
		//@@Descriptor Field - bTerminalID
		//@@bTerminalID should be greater than 0
		//@@Question: Should test to verify terminal number is valid
		AppendTextBuffer("*!*ERROR:  bTerminalID of %d is too small\r\n", VidOTDesc->bTerminalID);
		OOPS();
	}


	if (!(pStr))
	{
		//@@TestCase B4.3
		//@@ERROR
		//@@Descriptor Field - wTerminalType
		//@@No valid Terminal Type was found
		AppendTextBuffer("*!*ERROR:  0x%04X is an invalid wTerminalType for an Output Terminal\r\n",
			VidOTDesc->wTerminalType);
		OOPS();
	}

	//@@TestCase B4.4
	//@@Not yet implemented - Priority 1
	//@@Descriptor Field - bAssocTerminal
	//@@We should test to verify terminal number is valid
	//    AppendTextBuffer("bAssocTerminal:                    0x%02X\r\n", VidOTDesc->bAssocTerminal);

	if (VidOTDesc->bSourceID < 1)
	{
		//@@TestCase B4.5  (see Descript.c  line 333)
		//@@ERROR
		//@@Descriptor Field - bSourceID
		//@@bSourceID should be greater than 0
		//@@Question: Should test to verify source number is valid
		AppendTextBuffer("*!*ERROR:  bSourceID of %d is too small\r\n", VidOTDesc->bSourceID);
		OOPS();
	}

	switch (VidOTDesc->wTerminalType)
	{
	case 0x0100:  // TT_VENDOR_SPECIFIC Terminal Type
		break;
	case 0x0101:  // TT_STREAMING Terminal Type
		break;
	case 0x0300:  // OTT_VENDOR_SPECIFIC Terminal Type
		break;
	case 0x0301:  // OTT_DISPLAY Terminal Type
		break;
	case 0x0302:  // OTT_MEDIA_TRANSPORT_OUTPUT Terminal Type
		return DisplayVCMediaTransOutputTerminal(
			(PVIDEO_OUTPUT_MTT)VidOTDesc);
	case 0x0400:  // EXTERNAL_VENDOR_SPECIFIC Terminal Type
		break;
	case 0x0401:  // COMPOSITE_CONNECTOR Terminal Type
		break;
	case 0x0402:  // SVIDEO_CONNECTOR Terminal Type
		break;
	case 0x0403:  // COMPONENT_CONNECTOR Terminal Type
		break;
	default:
		break;
	}

	return TRUE;
}


//*****************************************************************************
//
// DisplayVCMediaTransInputTerminal()
//
//*****************************************************************************

BOOL
DisplayVCMediaTransInputTerminal(
	PVIDEO_INPUT_MTT MediaTransportInDesc
)
{
	//@@DisplayVCMediaTransInputTerminal -Video Control Media Transport Input Terminal
	UCHAR  p = 0;
	PUCHAR pData = NULL;
	size_t bLength = 0;

	bLength = SizeOfVideoInputMTT(MediaTransportInDesc);

	AppendTextBuffer("===>Additional Media Transport Input Terminal Data\r\n");
	AppendTextBuffer("bControlSize:                      0x%02X\r\n",
		MediaTransportInDesc->bControlSize);

	// point to bControlSize
	pData = &MediaTransportInDesc->bControlSize;

	// Are there any controls?
	if (0 < *pData)
	{
		UINT  uBitIndex = 0;
		BYTE  cCheckBit = 0;
		BYTE  cMask = 1;

		AppendTextBuffer("bmControls : ");
		VDisplayBytes(pData + 1, *pData);

		// map the first control    
		for (; uBitIndex < 8; uBitIndex++)
		{
			cCheckBit = cMask & *(pData + 1);

			AppendTextBuffer("     D%02d = %d  %s %s\r\n",
				uBitIndex,
				cCheckBit ? 1 : 0,
				cCheckBit ? "yes - " : " no - ",
				GetStringFromList(slMediaTransportControls,
					sizeof(slMediaTransportControls) / sizeof(STRINGLIST),
					cMask,
					"Invalid MediaTransportCtrl bmControl value"));

			cMask = cMask << 1;
		}
	}

	// point to bTransportModeSize
	pData = pData + 2;

	// Are there any controls?
	if (0 < *pData)
	{
		UINT  uBitIndex = 0;
		BYTE  cCheckBit = 0;
		BYTE  cMask = 1;

		AppendTextBuffer("bmControls : ");
		VDisplayBytes(pData + 1, *pData);

		// map the first control    
		for (; uBitIndex < 8; uBitIndex++)
		{
			cCheckBit = cMask & *(pData + 1);

			AppendTextBuffer("     D%02d = %d  %s %s\r\n",
				uBitIndex,
				cCheckBit ? 1 : 0,
				cCheckBit ? "yes - " : " no - ",
				GetStringFromList(slMediaTransportModes1,
					sizeof(slMediaTransportModes1) / sizeof(STRINGLIST),
					cMask,
					"Invalid MediaTransportMode value"));

			cMask = cMask << 1;
		}

		// Is there a second control?
		if (1 < *pData)
		{
			// map the second control   
			for (uBitIndex = 8, cMask = 1; uBitIndex < 16; uBitIndex++)
			{
				cCheckBit = cMask & *(pData + 2);

				AppendTextBuffer("     D%02d = %d  %s %s\r\n",
					uBitIndex,
					cCheckBit ? 1 : 0,
					cCheckBit ? "yes - " : " no - ",
					GetStringFromList(slMediaTransportModes2,
						sizeof(slMediaTransportModes2) / sizeof(STRINGLIST),
						cMask,
						"Invalid MediaTransportMode value"));

				cMask = cMask << 1;
			}
		}
		// Is there a third control?
		if (2 < *pData)
		{
			// map the third control    
			for (uBitIndex = 16, cMask = 1; uBitIndex < 24; uBitIndex++)
			{
				cCheckBit = cMask & *(pData + 3);

				AppendTextBuffer("     D%02d = %d  %s %s\r\n",
					uBitIndex,
					cCheckBit ? 1 : 0,
					cCheckBit ? "yes - " : " no - ",
					GetStringFromList(slMediaTransportModes3,
						sizeof(slMediaTransportModes3) / sizeof(STRINGLIST),
						cMask,
						"Invalid MediaTransportMode value"));

				cMask = cMask << 1;
			}
		}
		// Is there a fourth control?
		if (3 < *pData)
		{
			// map the fourth control   
			for (uBitIndex = 24, cMask = 1; uBitIndex < 32; uBitIndex++)
			{
				cCheckBit = cMask & *(pData + 4);

				AppendTextBuffer("     D%02d = %d  %s %s\r\n",
					uBitIndex,
					cCheckBit ? 1 : 0,
					cCheckBit ? "yes - " : " no - ",
					GetStringFromList(slMediaTransportModes4,
						sizeof(slMediaTransportModes4) / sizeof(STRINGLIST),
						cMask,
						"Invalid MediaTransportMode value"));

				cMask = cMask << 1;
			}
		}
		// Is there a fifth control?
		if (4 < *pData)
		{
			// map the fifth control   
			for (uBitIndex = 32, cMask = 1; uBitIndex < 40; uBitIndex++)
			{
				cCheckBit = cMask & *(pData + 5);

				AppendTextBuffer("     D%02d = %d  %s %s\r\n",
					uBitIndex,
					cCheckBit ? 1 : 0,
					cCheckBit ? "yes - " : " no - ",
					GetStringFromList(slMediaTransportModes5,
						sizeof(slMediaTransportModes5) / sizeof(STRINGLIST),
						cMask,
						"Invalid MediaTransportMode value"));

				cMask = cMask << 1;
			}
		}
	}

	// The size of a Media Transport Descriptor is 
	//   the size of the Descriptor plus
	//   (bControlSize - 1) plus
	//   IF bmControls & 1 THEN 1 (bTransportModeSize) plus
	//   bTransportModeSize
	//   
//    p = sizeof(VIDEO_INPUT_MTT) + 
//        (MediaTransportInDesc->bControlSize - 1);
//    if (MediaTransportInDesc->bmControls[0] & 1)
//        p += 1 + (*pData);
	if (MediaTransportInDesc->bLength != bLength)
	{
		//@@TestCase B5.1 (also in Descript.c)
		//@@ERROR
		//@@Descriptor Field - bLength
		//@@Invalid Descriptor length
		AppendTextBuffer("*!*ERROR:  Invalid descriptor bLength 0x%02X. "\
			"Should be 0x%02X\r\n",
			MediaTransportInDesc->bLength, p);
		OOPS();
	}

	return TRUE;
}


//*****************************************************************************
//
// DisplayVCMediaTransOutputTerminal()
//
//*****************************************************************************

BOOL
DisplayVCMediaTransOutputTerminal(
	PVIDEO_OUTPUT_MTT MediaTransportOutDesc
)
{
	//@@DisplayVCMediaTransOutputTerminal -Video Control Media Transport Output Terminal
	UCHAR  p = 0;
	PUCHAR pData = NULL;

	AppendTextBuffer("===>Additional Media Transport Output Terminal Data\r\n");
	AppendTextBuffer("bControlSize:                      0x%02X\r\n",
		MediaTransportOutDesc->bControlSize);

	// point to bControlSize
	pData = &MediaTransportOutDesc->bControlSize;

	// Are there any controls?
	if (0 < *pData)
	{
		UINT  uBitIndex = 0;
		BYTE  cCheckBit = 0;
		BYTE  cMask = 1;

		AppendTextBuffer("bmControls : ");
		VDisplayBytes(pData + 1, *pData);

		// map the first control    
		for (; uBitIndex < 8; uBitIndex++)
		{
			cCheckBit = cMask & *(pData + 1);

			AppendTextBuffer("     D%02d = %d  %s %s\r\n",
				uBitIndex,
				cCheckBit ? 1 : 0,
				cCheckBit ? "yes - " : " no - ",
				GetStringFromList(slMediaTransportControls,
					sizeof(slMediaTransportControls) / sizeof(STRINGLIST),
					cMask,
					"Invalid MediaTransportCtrl bmControl value"));

			cMask = cMask << 1;
		}
	}

	// point to bTransportModeSize
	pData = pData + 2;

	// Are there any controls?
	if (0 < *pData)
	{
		UINT  uBitIndex = 0;
		BYTE  cCheckBit = 0;
		BYTE  cMask = 1;

		AppendTextBuffer("bmControls : ");
		VDisplayBytes(pData + 1, *pData);

		// map the first control    
		for (; uBitIndex < 8; uBitIndex++)
		{
			cCheckBit = cMask & *(pData + 1);

			AppendTextBuffer("     D%02d = %d  %s %s\r\n",
				uBitIndex,
				cCheckBit ? 1 : 0,
				cCheckBit ? "yes - " : " no - ",
				GetStringFromList(slMediaTransportModes1,
					sizeof(slMediaTransportModes1) / sizeof(STRINGLIST),
					cMask,
					"Invalid MediaTransportMode value"));

			cMask = cMask << 1;
		}

		// Is there a second control?
		if (1 < *pData)
		{
			// map the second control   
			for (uBitIndex = 8, cMask = 1; uBitIndex < 16; uBitIndex++)
			{
				cCheckBit = cMask & *(pData + 2);

				AppendTextBuffer("     D%02d = %d  %s %s\r\n",
					uBitIndex,
					cCheckBit ? 1 : 0,
					cCheckBit ? "yes - " : " no - ",
					GetStringFromList(slMediaTransportModes2,
						sizeof(slMediaTransportModes2) / sizeof(STRINGLIST),
						cMask,
						"Invalid MediaTransportMode value"));

				cMask = cMask << 1;
			}
		}
		// Is there a third control?
		if (2 < *pData)
		{
			// map the third control    
			for (uBitIndex = 16, cMask = 1; uBitIndex < 24; uBitIndex++)
			{
				cCheckBit = cMask & *(pData + 3);

				AppendTextBuffer("     D%02d = %d  %s %s\r\n",
					uBitIndex,
					cCheckBit ? 1 : 0,
					cCheckBit ? "yes - " : " no - ",
					GetStringFromList(slMediaTransportModes3,
						sizeof(slMediaTransportModes3) / sizeof(STRINGLIST),
						cMask,
						"Invalid MediaTransportMode value"));

				cMask = cMask << 1;
			}
		}
		// Is there a fourth control?
		if (3 < *pData)
		{
			// map the fourth control   
			for (uBitIndex = 24, cMask = 1; uBitIndex < 32; uBitIndex++)
			{
				cCheckBit = cMask & *(pData + 4);

				AppendTextBuffer("     D%02d = %d  %s %s\r\n",
					uBitIndex,
					cCheckBit ? 1 : 0,
					cCheckBit ? "yes - " : " no - ",
					GetStringFromList(slMediaTransportModes4,
						sizeof(slMediaTransportModes4) / sizeof(STRINGLIST),
						cMask,
						"Invalid MediaTransportMode value"));

				cMask = cMask << 1;
			}
		}
		// Is there a fifth control?
		if (4 < *pData)
		{
			// map the fourth control   
			for (uBitIndex = 32, cMask = 1; uBitIndex < 40; uBitIndex++)
			{
				cCheckBit = cMask & *(pData + 5);

				AppendTextBuffer("     D%02d = %d  %s %s\r\n",
					uBitIndex,
					cCheckBit ? 1 : 0,
					cCheckBit ? "yes - " : " no - ",
					GetStringFromList(slMediaTransportModes5,
						sizeof(slMediaTransportModes5) / sizeof(STRINGLIST),
						cMask,
						"Invalid MediaTransportMode value"));

				cMask = cMask << 1;
			}
		}
	}

	// The size of a Media Transport Descriptor is 
	//   the size of the Descriptor plus
	//   (bControlSize - 1) plus
	//   IF bmControls & 1 THEN 1 (bTransportModeSize) plus
	//   bTransportModeSize
	//   
	p = sizeof(VIDEO_OUTPUT_MTT) +
		(MediaTransportOutDesc->bControlSize - 1);
	if (MediaTransportOutDesc->bmControls[0] & 1)
		p += 1 + (*pData);
	if (MediaTransportOutDesc->bLength != p)
	{
		//@@TestCase B5.1 (also in Descript.c)
		//@@ERROR
		//@@Descriptor Field - bLength
		//@@Invalid Descriptor length
		AppendTextBuffer("*!*ERROR:  Invalid descriptor bLength 0x%02X. "\
			"Should be 0x%02X\r\n",
			MediaTransportOutDesc->bLength, p);
		OOPS();
	}

	return TRUE;
}


//*****************************************************************************
//
// DisplayVCCameraTerminal()
//
//*****************************************************************************

BOOL
DisplayVCCameraTerminal(
	PVIDEO_CAMERA_TERMINAL CameraDesc
)
{
	//@@DisplayVCCameraTerminal -Video Control Camera Terminal
	UCHAR  p = 0;
	PUCHAR pData = NULL;

	AppendTextBuffer("===>Camera Input Terminal Data\r\n");
	AppendTextBuffer("wObjectiveFocalLengthMin:        0x%04X\r\n", CameraDesc->wObjectiveFocalLengthMin);
	AppendTextBuffer("wObjectiveFocalLengthMax:        0x%04X\r\n", CameraDesc->wObjectiveFocalLengthMax);
	AppendTextBuffer("wOcularFocalLength:              0x%04X\r\n", CameraDesc->wOcularFocalLength);
	AppendTextBuffer("bControlSize:                      0x%02X\r\n", CameraDesc->bControlSize);

	pData = &CameraDesc->bControlSize;

	// Are there any controls?
	if (0 < *pData)
	{
		UINT  uBitIndex = 0;
		BYTE  cCheckBit = 0;
		BYTE  cMask = 1;

		AppendTextBuffer("bmControls : ");
		VDisplayBytes(pData + 1, *pData);

		// map the first control    
		for (; uBitIndex < 8; uBitIndex++)
		{
			cCheckBit = cMask & *(pData + 1);

			AppendTextBuffer("     D%02d = %d  %s %s\r\n",
				uBitIndex,
				cCheckBit ? 1 : 0,
				cCheckBit ? "yes - " : " no - ",
				GetStringFromList(slCameraControl1,
					sizeof(slCameraControl1) / sizeof(STRINGLIST),
					cMask,
					"Invalid CamCtrl bmControl value"));

			cMask = cMask << 1;
		}

		// Is there a second control?
		if (1 < *pData)
		{
			// map the second control   
			for (uBitIndex = 8, cMask = 1; uBitIndex < 16; uBitIndex++)
			{
				cCheckBit = cMask & *(pData + 2);

				AppendTextBuffer("     D%02d = %d  %s %s\r\n",
					uBitIndex,
					cCheckBit ? 1 : 0,
					cCheckBit ? "yes - " : " no - ",
					GetStringFromList(slCameraControl2,
						sizeof(slCameraControl2) / sizeof(STRINGLIST),
						cMask,
						"Invalid CamCtrl bmControl value"));

				cMask = cMask << 1;
			}
		}
		// Is there a third control?
		if (2 < *pData)
		{
			// map the third control    
			for (uBitIndex = 16, cMask = 1; uBitIndex < 24; uBitIndex++)
			{
				cCheckBit = cMask & *(pData + 3);

				AppendTextBuffer("     D%02d = %d  %s %s\r\n",
					uBitIndex,
					cCheckBit ? 1 : 0,
					cCheckBit ? "yes - " : " no - ",
					GetStringFromList(slCameraControl3,
						sizeof(slCameraControl3) / sizeof(STRINGLIST),
						cMask,
						"Invalid CamCtrl bmControl value"));

				cMask = cMask << 1;
			}
		}
	}

	p = (sizeof(VIDEO_CAMERA_TERMINAL) + CameraDesc->bControlSize);
	if (CameraDesc->bLength != p)
	{
		//@@TestCase B7.1 (also in Descript.c)
		//@@ERROR
		//@@Descriptor Field - bLength
		//@@The descriptor should be the size of the descriptor structure 
		//@@  plus the number of controls
		AppendTextBuffer("*!*ERROR:  bLength of %d incorrect, should be %d\r\n",
			CameraDesc->bLength, p);
		OOPS();
	}

	//@@TestCase B7.2
	//@@Not yet implemented - Priority 3
	//@@Descriptor Field - wObjectiveFocalLengthMin
	//@@Question - Should we do any checking here?  What are the acceptable boundaries?
	//@@Question - Is zero an acceptable value?
	//    AppendTextBuffer("wObjectiveFocalLengthMin:        0x%04X\r\n", CameraDesc->wObjectiveFocalLengthMin);

	//@@TestCase B7.3
	//@@Not yet implemented - Priority 3
	//@@Descriptor Field - wObjectiveFocalLengthMax
	//@@Question - Should we do any checking here?  What are the acceptable boundaries
	//@@Question - Is zero an acceptable value?
	//    AppendTextBuffer("wObjectiveFocalLengthMax:        0x%04X\r\n", CameraDesc->wObjectiveFocalLengthMax);

	//@@TestCase B7.4
	//@@Not yet implemented - Priority 3
	//@@Descriptor Field - wOcularFocalLength
	//@@Question - Should we do any checking here?  What are the acceptable boundaries
	//@@Question - Is zero an acceptable value?
	//    AppendTextBuffer("wOcularFocalLength:              0x%04X\r\n", CameraDesc->wOcularFocalLength);

	//@@TestCase B7.5
	//@@ERROR
	//@@Descriptor Field - wObjectiveFocalLengthMin and wObjectiveFocalLengthMax
	//@@Verify that wObjectiveFocalLengthMax is greater than wObjectiveFocalLengthMin
	if (CameraDesc->wObjectiveFocalLengthMin > CameraDesc->wObjectiveFocalLengthMax)
	{
		AppendTextBuffer("*!*ERROR:  wObjectiveFocalLengthMin is larger than wObjectiveFocalLengthMax\r\n");
		OOPS();
	}

	//@@TestCase B7.6
	//@@ERROR
	//@@Descriptor Field - bControlSize
	//@@Verify that wObjectiveFocalLengthMax is 3 or less
	if (CameraDesc->bControlSize > 3)
	{
		AppendTextBuffer("*!*ERROR:  bControlSize must be 3 or less\r\n");
		OOPS();
	}
	return TRUE;
}

//*****************************************************************************
//
// DisplayVCSelectorUnit()
//
//*****************************************************************************

BOOL
DisplayVCSelectorUnit(
	PVIDEO_SELECTOR_UNIT    VidSelectorDesc,
	PSTRING_DESCRIPTOR_NODE StringDescs,
	DEVICE_POWER_STATE      LatestDevicePowerState
)
{
	//@@DisplayVCSelectorUnit -Video Control Selector Unit
	UCHAR  i = 0;
	UCHAR  p = 0;
	PUCHAR pData = NULL;

	AppendTextBuffer("\r\n          ===>Video Control Selector Unit Descriptor<===\r\n");
	AppendTextBuffer("bLength:                           0x%02X\r\n", VidSelectorDesc->bLength);
	AppendTextBuffer("bDescriptorType:                   0x%02X\r\n", VidSelectorDesc->bDescriptorType);
	AppendTextBuffer("bDescriptorSubtype:                0x%02X\r\n", VidSelectorDesc->bDescriptorSubtype);
	AppendTextBuffer("bUnitID:                           0x%02X\r\n", VidSelectorDesc->bUnitID);
	AppendTextBuffer("bNrInPins:                         0x%02X\r\n", VidSelectorDesc->bNrInPins);
	if (gDoAnnotation)
	{
		AppendTextBuffer("===>List of Connected Unit and Terminal ID's\r\n");
	}
	// baSourceID is a variable length field
	// Size is in bNrInPins, must be at least 1 (so index starts at 1)
	for (i = 1, pData = (PUCHAR)&VidSelectorDesc->baSourceID;
		i <= VidSelectorDesc->bNrInPins; i++, pData++)
	{
		AppendTextBuffer("baSourceID[%d]:                     0x%02X\r\n",
			i, *pData);
	}

	// get address of iSelector, the last field in this descriptor
	pData = (PUCHAR)VidSelectorDesc + (VidSelectorDesc->bLength - 1);
	AppendTextBuffer("iSelector:                         0x%02X\r\n", *pData);
	if (gDoAnnotation)
	{
		if (*pData)
		{
			// if executing this code, the configuration descriptor has been 
			// obtained.  If a device is suspended, then its configuration
			// descriptor was not obtained and we do not want errors to be 
			// displayed when string descriptors were not obtained.
			DisplayStringDescriptor(*pData, StringDescs, LatestDevicePowerState);
		}
	}

	p = (sizeof(VIDEO_SELECTOR_UNIT) + VidSelectorDesc->bNrInPins + 1);
	if (VidSelectorDesc->bLength != p)
	{
		//@@TestCase B8.1 (also in Descript.c)
		//@@ERROR
		//@@Descriptor Field - bLength
		//@@The descriptor should be the size of the descriptor structure plus the number of pins
		AppendTextBuffer("*!*ERROR:  bLength of %d incorrect, should be %d\r\n",
			VidSelectorDesc->bLength, p);
		OOPS();
	}

	if (VidSelectorDesc->bUnitID < 1)
	{
		//@@TestCase B8.2 (Descript.c   Line 396)
		//@@ERROR
		//@@Descriptor Field - bUnitID
		//@@bUnitID must be greater than 0
		//@@Question: Should we test to verify unit number is unique?
		AppendTextBuffer("*!*ERROR:  bUnitID must be non-zero\r\n");
		OOPS();
	}

	if (VidSelectorDesc->bNrInPins < 1)
	{
		//@@TestCase B8.3
		//@@ERROR
		//@@Descriptor Field - bNrInPins
		//@@bNrInPins should be greater than 0
		//@@Question: Should test to verify total in pins is valid
		AppendTextBuffer("*!*ERROR:  bNrInPins must be non-zero\r\n");
		OOPS();
	}

	// baSourceID is a variable length field
	// Size is in bNrInPins, must be at least 1 (so index starts at 1)
	for (i = 1, pData = (PUCHAR)&VidSelectorDesc->baSourceID;
		i <= VidSelectorDesc->bNrInPins; i++, pData++)
	{
		if (*pData < 1)
		{
			//@@TestCase B8.4
			//@@ERROR
			//@@Descriptor Field - baSourceID[]
			//@@baSourceID should be greater than 0
			AppendTextBuffer("*!*ERROR:  baSourceID[%d] must be non-zero\r\n", i);
			OOPS();
		}
		else {
			if (!ValidateTerminalID(*pData)) {
				//@@TestCase B8.5
				//@@ERROR
				//@@Descriptor Field - baSourceID[]
				//@@baSourceID should be a valid terminal ID
				AppendTextBuffer("*!*ERROR:  baSourceID[%d] must be non-zero\r\n", i);
				OOPS();
			}
		}
	}

	return TRUE;
}


//*****************************************************************************
//
// DisplayVCProcessingUnit()
//
//*****************************************************************************

BOOL
DisplayVCProcessingUnit(
	PVIDEO_PROCESSING_UNIT  VidProcessingDesc,
	PSTRING_DESCRIPTOR_NODE StringDescs,
	DEVICE_POWER_STATE      LatestDevicePowerState
)
{
	//@@DisplayVCProcessingUnit -Video Control Processor Unit
	PUCHAR pData = NULL;
	UCHAR  bLength = 0;

	AppendTextBuffer("\r\n          ===>Video Control Processing Unit Descriptor<===\r\n");
	AppendTextBuffer("bLength:                           0x%02X\r\n", VidProcessingDesc->bLength);
	AppendTextBuffer("bDescriptorType:                   0x%02X\r\n", VidProcessingDesc->bDescriptorType);
	AppendTextBuffer("bDescriptorSubtype:                0x%02X\r\n", VidProcessingDesc->bDescriptorSubtype);
	AppendTextBuffer("bUnitID:                           0x%02X\r\n", VidProcessingDesc->bUnitID);
	AppendTextBuffer("bSourceID:                         0x%02X\r\n", VidProcessingDesc->bSourceID);
	AppendTextBuffer("wMaxMultiplier:                  0x%04X\r\n", VidProcessingDesc->wMaxMultiplier);
	AppendTextBuffer("bControlSize:                      0x%02X\r\n", VidProcessingDesc->bControlSize);

	pData = &VidProcessingDesc->bControlSize;

	// Are there any controls?
	if (0 < *pData)
	{
		UINT  uBitIndex = 0;
		BYTE  cCheckBit = 0;
		BYTE  cMask = 1;

		AppendTextBuffer("bmControls : ");
		VDisplayBytes(pData + 1, *pData);

		// map the first control    
		for (; uBitIndex < 8; uBitIndex++)
		{
			cCheckBit = cMask & *(pData + 1);

			AppendTextBuffer("     D%02d = %d  %s %s\r\n",
				uBitIndex,
				cCheckBit ? 1 : 0,
				cCheckBit ? "yes - " : " no - ",
				GetStringFromList(slProcessorControls1,
					sizeof(slProcessorControls1) / sizeof(STRINGLIST),
					cMask,
					"Invalid PU bmControl value"));

			cMask = cMask << 1;
		}

		// Is there a second control?
		if (1 < *pData)
		{
			// map the second control   
			for (uBitIndex = 8, cMask = 1; uBitIndex < 16; uBitIndex++)
			{
				cCheckBit = cMask & *(pData + 2);

				AppendTextBuffer("     D%02d = %d  %s %s\r\n",
					uBitIndex,
					cCheckBit ? 1 : 0,
					cCheckBit ? "yes - " : " no - ",
					GetStringFromList(slProcessorControls2,
						sizeof(slProcessorControls2) / sizeof(STRINGLIST),
						cMask,
						"Invalid PU bmControl value"));

				cMask = cMask << 1;
			}
		}

		// Is there a third control?
		if (2 < *pData)
		{
			// map the third control 
			for (uBitIndex = 16, cMask = 1; uBitIndex < 24; uBitIndex++)
			{
				cCheckBit = cMask & *(pData + 3);

				AppendTextBuffer("     D%02d = %d  %s %s\r\n",
					uBitIndex,
					cCheckBit ? 1 : 0,
					cCheckBit ? "yes - " : " no - ",
					GetStringFromList(slProcessorControls3,
						sizeof(slProcessorControls3) / sizeof(STRINGLIST),
						cMask,
						"Invalid PU bmControl value"));

				cMask = cMask << 1;
			}
		}
	}

	// get address of iProcessing
	if (UVC10 != g_chUVCversion)
	{
		// size of descriptor is struct size plus control size plus 2 if UVC11
		bLength = sizeof(VIDEO_PROCESSING_UNIT) + 2 + VidProcessingDesc->bControlSize;
		pData = (PUCHAR)VidProcessingDesc + (VidProcessingDesc->bLength - 2);
	}
	else // UVC 1.0
	{
		// size of descriptor is struct size plus control size plus 1 if UVC10
		bLength = sizeof(VIDEO_PROCESSING_UNIT) + 1 + VidProcessingDesc->bControlSize;
		pData = (PUCHAR)VidProcessingDesc + (VidProcessingDesc->bLength - 1);
	}
	AppendTextBuffer("iProcessing :                      0x%02X\r\n", *pData);
	if (gDoAnnotation)
	{
		if (*pData)
		{
			// if executing this code, the configuration descriptor has been 
			// obtained.  If a device is suspended, then its configuration
			// descriptor was not obtained and we do not want errors to be 
			// displayed when string descriptors were not obtained.
			DisplayStringDescriptor(*pData, StringDescs, LatestDevicePowerState);
		}
	}

	// check for new UVC 1.1 bmVideoStandards fields
	if (UVC10 != g_chUVCversion)
	{
		UINT  uBitIndex = 0;
		BYTE  cCheckBit = 0;
		BYTE  cMask = 1;

		pData = (PUCHAR)VidProcessingDesc + (VidProcessingDesc->bLength - 1);

		AppendTextBuffer("bmVideoStandards :                 ");
		VDisplayBytes(pData, 1);

		// map the first control    
		for (; uBitIndex < 8; uBitIndex++)
		{
			cCheckBit = cMask & *(pData);

			AppendTextBuffer("     D%02d = %d  %s %s\r\n",
				uBitIndex,
				cCheckBit ? 1 : 0,
				cCheckBit ? "yes - " : " no - ",
				GetStringFromList(slProcessorVideoStandards,
					sizeof(slProcessorVideoStandards) / sizeof(STRINGLIST),
					cMask,
					"Invalid PU bmVideoStandards value"));

			cMask = cMask << 1;
		}
	}

	if (VidProcessingDesc->bLength != bLength)
	{
		//@@TestCase B9.1 (also in Descript.c)
		//@@ERROR
		//@@Descriptor Field - bLength
		AppendTextBuffer("*!*ERROR:  bLength of 0x%02X incorrect, should be 0x%02X\r\n",
			VidProcessingDesc->bLength, bLength);
		OOPS();
	}

	if (VidProcessingDesc->bUnitID < 1)
	{
		//@@TestCase B9.2 (Descript.c   Line 466)
		//@@ERROR
		//@@Descriptor Field - bUnitID
		//@@bUnitID must be greater than 0
		//@@Question: Should we test to verify unit number is unique?
		AppendTextBuffer("*!*ERROR:  bUnitID must be non-zero\r\n");
		OOPS();
	}

	if (VidProcessingDesc->bSourceID < 1)
	{
		//@@TestCase B9.3 (Descript.c   Line 471)
		//@@ERROR
		//@@Descriptor Field - bSourceID
		//@@bSourceID must be non-zero
		//@@Question: Should we test to verify the bSourceID is valid?
		AppendTextBuffer("*!*ERROR:  bSourceID must be non-zero\r\n");
		OOPS();
	}

	//@@TestCase B9.4
	//@@Not yet implemented - Priority 1
	//@@Descriptor Field - wMaxMultiplier
	//@@We should test to verify multiplier is valid
	//    AppendTextBuffer("wMaxMultiplier:                  0x%04X\r\n", VidProcessingDesc->wMaxMultiplier);

	return TRUE;
}


//*****************************************************************************
//
// DisplayVCExtensionUnit()
//
//*****************************************************************************

BOOL
DisplayVCExtensionUnit(
	PVIDEO_EXTENSION_UNIT   VidExtensionDesc,
	PSTRING_DESCRIPTOR_NODE StringDescs,
	DEVICE_POWER_STATE      LatestDevicePowerState
)
{
	//@@DisplayVCExtensionUnit -Video Control Extension Unit
	int     i = 0;
	UCHAR   p = 0;
	UCHAR   bControlSize = 0;
	PUCHAR  pData = NULL;
	OLECHAR szGUID[256];
	size_t  bLength = 0;

	bLength = SizeOfVideoExtensionUnit(VidExtensionDesc);

	memset((LPOLESTR)szGUID, 0, sizeof(OLECHAR) * 256);
	i = StringFromGUID2((REFGUID)&VidExtensionDesc->guidExtensionCode, (LPOLESTR)szGUID, 255);
	i++;

	AppendTextBuffer("\r\n          ===>Video Control Extension Unit Descriptor<===\r\n");
	AppendTextBuffer("bLength:                           0x%02X\r\n", VidExtensionDesc->bLength);
	AppendTextBuffer("bDescriptorType:                   0x%02X\r\n", VidExtensionDesc->bDescriptorType);
	AppendTextBuffer("bDescriptorSubtype:                0x%02X\r\n", VidExtensionDesc->bDescriptorSubtype);
	AppendTextBuffer("bUnitID:                           0x%02X\r\n", VidExtensionDesc->bUnitID);
	AppendTextBuffer("guidExtensionCode:                 %S\r\n", szGUID);
	AppendTextBuffer("bNumControls:                      0x%02X\r\n", VidExtensionDesc->bNumControls);
	AppendTextBuffer("bNrInPins:                         0x%02X\r\n", VidExtensionDesc->bNrInPins);
	if (gDoAnnotation)
	{
		AppendTextBuffer("===>List of Connected Units and Terminal ID's\r\n");
	}
	// baSourceID is a variable length field
	// Size is in bNrInPins, must be at least 1 (so index starts at 1)
	for (i = 1, pData = (PUCHAR)&VidExtensionDesc->baSourceID;
		i <= VidExtensionDesc->bNrInPins; i++, pData++)
	{
		AppendTextBuffer("baSourceID[%d]:                     0x%02X\r\n",
			i, *pData);
	}
	// point to bControlSize (address of bNrInPins plus number of fields in bNrInPins
	//   plus 1 for next field)
	pData = &VidExtensionDesc->bNrInPins + VidExtensionDesc->bNrInPins + 1;
	bControlSize = *pData;
	AppendTextBuffer("bControlSize:                      0x%02X\r\n", bControlSize);

	// Are there any controls?
	if (bControlSize > 0)
	{
		AppendTextBuffer("bmControls : ");
		VDisplayBytes(pData + 1, *pData);

		// Map one byte at a time of the bmControls field in the Video Control Extension Unit Descriptor
		for (i = 1; i <= bControlSize; i++)
		{
			UINT  uBitIndex = 0;
			BYTE  cCheckBit = 0;
			BYTE  cMask = 1;

			// map byte    
			for (; uBitIndex < 8; uBitIndex++)
			{
				cCheckBit = cMask & *(pData + i);

				AppendTextBuffer("     D%02d = %d  %s %s\r\n",
					uBitIndex + 8 * (i - 1),
					cCheckBit ? 1 : 0,
					cCheckBit ? "yes - " : " no - ",
					"Vendor-Specific (Optional)");

				cMask = cMask << 1;
			}
		}
	}

	// get address of iExtension
	pData = &VidExtensionDesc->bNrInPins + VidExtensionDesc->bNrInPins + bControlSize + 2;
	//  pData = (PUCHAR) VidExtensionDesc + (VidExtensionDesc->bLength - 1);
	AppendTextBuffer("iExtension:                        0x%02X\r\n", *pData);
	if (gDoAnnotation)
	{
		if (*pData)
		{
			DisplayStringDescriptor(*pData, StringDescs, LatestDevicePowerState);
		}
	}

	// size of descriptor struct size (23) + bNrInPins + bControlSize + iExtension size
	// 
//  p = (sizeof(VIDEO_EXTENSION_UNIT) 
//      + VidExtensionDesc->bNrInPins + bControlSize + 1);
	if (VidExtensionDesc->bLength != bLength)
	{
		//@@TestCase B10.1 (also in Descript.c)
		//@@ERROR
		//@@Descriptor Field - bLength
		//@@The declared length in the device descriptor is not equal to the 
		//@@  required length in the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  bLength of 0x%02X incorrect, should be 0x%02X\r\n",
			VidExtensionDesc->bLength, p);
		OOPS();
	}

	if (VidExtensionDesc->bUnitID < 1)
	{
		//@@TestCase B10.2 (Descript.c  Line 517)
		//@@ERROR
		//@@Descriptor Field - bUnitID
		//@@bUnitID must be non-zero
		//@@Question: Should we test to verify bUnitID is valid
		AppendTextBuffer("*!*ERROR:  bUnitID must be non-zero\r\n");
		OOPS();
	}

	//bugbug do we need two
	if (VidExtensionDesc->bNrInPins < 1)
	{
		//@@TestCase B10.3 (Descript.c  Line 522)
		//@@ERROR
		//@@Descriptor Field - bNrInPins
		//@@bNrInPins must be non-zero
		//@@Question: Should we test to verify bNrInPins is valid
		AppendTextBuffer("*!*ERROR:  bNrInPins must be non-zero\r\n");
		OOPS();
	}

	for (i = 1, pData = (PUCHAR)&VidExtensionDesc->baSourceID;
		i <= VidExtensionDesc->bNrInPins; i++, pData++)
	{
		if (*pData == 0)
		{
			//@@TestCase B10.4  (Descript.c  Line 527)
			//@@ERROR
			//@@Descriptor Field - baSourceID[]
			//@@baSourceID[] must be non-zero
			//@@Question: Should we test to verify baSourceID is valid
			AppendTextBuffer("*!*ERROR:  baSourceID[%d] must be non-zero\r\n", *pData);
			OOPS();
		}
	}
	return TRUE;
}

//*****************************************************************************
//
// DisplayVidInHeaderl()
//
//*****************************************************************************

BOOL
DisplayVidInHeader(
	PVIDEO_STREAMING_INPUT_HEADER VidInHeaderDesc
)
{
	//@@DisplayVidInHeader -Video Streaming Video Input Header
	UINT   p = 0;
	UINT   uCount = 0;
	PUCHAR pData = NULL;

	AppendTextBuffer("\r\n          ===>Video Class-Specific VS Video Input Header Descriptor<===\r\n");
	AppendTextBuffer("bLength:                           0x%02X\r\n", VidInHeaderDesc->bLength);
	AppendTextBuffer("bDescriptorType:                   0x%02X\r\n", VidInHeaderDesc->bDescriptorType);
	AppendTextBuffer("bDescriptorSubtype:                0x%02X\r\n", VidInHeaderDesc->bDescriptorSubtype);
	AppendTextBuffer("bNumFormats:                       0x%02X\r\n", VidInHeaderDesc->bNumFormats);
	AppendTextBuffer("wTotalLength:                    0x%04X", VidInHeaderDesc->wTotalLength);

	uCount = GetVSInterfaceSize((PUSB_COMMON_DESCRIPTOR)VidInHeaderDesc, VidInHeaderDesc->wTotalLength);
	if (uCount != VidInHeaderDesc->wTotalLength) {
		AppendTextBuffer("\r\n*!*ERROR:  invalid interface size 0x%02X, should be 0x%02X\r\n",
			VidInHeaderDesc->wTotalLength, uCount);
	}
	else {
		AppendTextBuffer("  -> Validated\r\n");
	}

	AppendTextBuffer("bEndpointAddress:                  0x%02X",
		VidInHeaderDesc->bEndpointAddress);
	if (USB_ENDPOINT_DIRECTION_IN(VidInHeaderDesc->bEndpointAddress))
	{
		if (gDoAnnotation)
		{
			AppendTextBuffer("  -> Direction: IN - EndpointID: %d",
				(VidInHeaderDesc->bEndpointAddress & 0x0F));
		}
		AppendTextBuffer("\r\n");
	}
	AppendTextBuffer("bmInfo:                            0x%02X", VidInHeaderDesc->bmInfo);
	if (gDoAnnotation)
	{
		AppendTextBuffer("  -> Dynamic Format Change %sSupported",
			!(VidInHeaderDesc->bmInfo & 0x01) ? "not " : " ");
	}
	AppendTextBuffer("\r\nbTerminalLink:                     0x%02X\r\n",
		VidInHeaderDesc->bTerminalLink);
	AppendTextBuffer("bStillCaptureMethod:               0x%02X",
		VidInHeaderDesc->bStillCaptureMethod);

	// globally save the StillMethod, then verify value
	StillMethod = VidInHeaderDesc->bStillCaptureMethod;
	if (StillMethod > 3)
	{
		//@@TestCase B11.1 (Descript.c Line 798)
		//@@ERROR
		//@@Descriptor Field - bStillCaptureMethod
		//@@bStillCaptureMethod is greater than 3
		AppendTextBuffer("*!*ERROR:  invalid bStillCaptureMethod 0x%02X\r\n",
			VidInHeaderDesc->bStillCaptureMethod);
		if (gDoAnnotation)
		{
			AppendTextBuffer("  -> Invalid Still Capture Method");
		}
	}
	else
	{
		if (0 == StillMethod)
		{
			AppendTextBuffer("  -> No Still Capture");
		}
		else
		{
			AppendTextBuffer("  -> Still Capture Method %d",
				VidInHeaderDesc->bStillCaptureMethod);
		}
	}

	AppendTextBuffer("\r\nbTriggerSupport:                   0x%02X",
		VidInHeaderDesc->bTriggerSupport);
	if (gDoAnnotation)
	{
		AppendTextBuffer("  -> ");
		if (!VidInHeaderDesc->bTriggerSupport)
			AppendTextBuffer("No ");
		AppendTextBuffer("Hardware Triggering Support");
	}
	AppendTextBuffer("\r\n");

	AppendTextBuffer("bTriggerUsage:                     0x%02X",
		VidInHeaderDesc->bTriggerUsage);
	if (gDoAnnotation)
	{
		if (VidInHeaderDesc->bTriggerSupport != 0)
		{
			if (VidInHeaderDesc->bTriggerUsage == 0)
				AppendTextBuffer("  -> Host will initiate still image capture");
			if (VidInHeaderDesc->bTriggerUsage == 1)
				AppendTextBuffer("  -> Host will notify client application of button event");
		}
	}

	AppendTextBuffer("\r\nbControlSize:                      0x%02X\r\n",
		VidInHeaderDesc->bControlSize);

	// are there formats to display?
	if (VidInHeaderDesc->bNumFormats)
	{
		UINT   uFormatIndex = 1;
		UINT   uBitIndex = 0;
		BYTE   cCheckBit = 0;
		BYTE   cMask = 1;

		// There are (bNumFormats) bmaControls fields, each with size (bControlSize)
		pData = (PUCHAR) & (VidInHeaderDesc->bControlSize);

		// VidInHeaderDesc->bNumFormats  -> number of formats
		// VidInHeaderDesc->bControlSize -> size of EACH format control
		// ((PUCHAR) &VidInHeaderDesc->bControlSize) + 1 -> address of first format control
		for (pData++; uFormatIndex <= VidInHeaderDesc->bNumFormats; uFormatIndex++)
		{
			AppendTextBuffer("Video Payload Format %d             ", uFormatIndex);

			// Handle case of 0 control size
			if (!VidInHeaderDesc->bControlSize)
			{
				AppendTextBuffer("0x00\r\n");
			}
			else
			{
				VDisplayBytes(pData, VidInHeaderDesc->bControlSize);

				// map the first control    
				for (uBitIndex = 0, cMask = 1; uBitIndex < 8; uBitIndex++)
				{
					cCheckBit = cMask & *(pData);

					AppendTextBuffer("     D%02d = %d  %s %s\r\n",
						uBitIndex,
						cCheckBit ? 1 : 0,
						cCheckBit ? "yes - " : " no - ",
						GetStringFromList(slInputHeaderControls,
							sizeof(slInputHeaderControls) / sizeof(STRINGLIST),
							cMask,
							"Invalid Control value"));

					cMask = cMask << 1;
				}
			}
			pData += VidInHeaderDesc->bControlSize;
		}
	}

	p = (sizeof(VIDEO_STREAMING_INPUT_HEADER) +
		(VidInHeaderDesc->bNumFormats * VidInHeaderDesc->bControlSize));
	if (VidInHeaderDesc->bLength != p)
	{
		//@@TestCase B11.2  (also in Descript.c)
		//@@ERROR
		//@@Descriptor Field - bLength
		//@@The descriptor should be the size of the descriptor structure 
		//@@  plus the number of formats times the size of each format 
		AppendTextBuffer("*!*ERROR:  bLength of %d incorrect, should be %d\r\n",
			VidInHeaderDesc->bLength, p);
		OOPS();
	}

	if (VidInHeaderDesc->bNumFormats < 1)
	{
		//@@TestCase B11.3 (Descript.c  Line778)
		//@@ERROR
		//@@Descriptor Field - bNumFormats
		//@@bNumFormats must be non-zero
		//@@Question: Should we test to verify the non-zero value for bNumFormats is valid
		AppendTextBuffer("*!*ERROR:  bNumFormats must be non-zero\r\n",
			VidInHeaderDesc->bNumFormats);
		OOPS();
	}

	if (VidInHeaderDesc->bEndpointAddress < 1)
	{
		//@@TestCase B11.4  (Descript.c  Line788)
		//@@ERROR
		//@@Descriptor Field - bEndpointAddress
		//@@bEndpointAddress should be greater than 0
		//@@Question: Should we test to verify the non-zero value for bEndpointAddress is valid
		AppendTextBuffer("*!*ERROR:  bEndpointAddress of %d is too small\r\n",
			VidInHeaderDesc->bEndpointAddress);
		OOPS();
	}

	//@@TestCase B11.5
	//@@ERROR
	//@@Descriptor Field - bEndPointAddress
	//@@The bEndPointAddress is set incorrectly according to the USB Video Device Specification
	if (!USB_ENDPOINT_DIRECTION_IN(VidInHeaderDesc->bEndpointAddress)) {
		AppendTextBuffer("\r\n*!*ERROR:  bEndPointAddress needs to have the Direction IN for this header\r\n");
		OOPS();
	}

	//@@TestCase B11.6
	//@@Not yet implemented - Priority 1
	//@@Descriptor Field - bmInfo
	//@@We should validate that reserved bits are set to zero.
	//    AppendTextBuffer("bmInfo:                            0x%02X", VidInHeaderDesc->bmInfo);

	if (VidInHeaderDesc->bTerminalLink < 1)
	{
		//@@TestCase B11.7 (Descript.c  Line 793)
		//@@ERROR
		//@@Descriptor Field - bTerminalLink
		//@@bTerminalLink should be greater than 0
		//@@Question: Should we test to verify the non-zero value for bTerminalLink is valid
		AppendTextBuffer("*!*ERROR:  bTerminalLink of %d is too small\r\n",
			VidInHeaderDesc->bTerminalLink);
		OOPS();
	}

	//@@TestCase B11.8
	//@@Not yet implemented - Priority 1
	//@@Descriptor Field - bTriggerSupport
	//@@We should validate that reserved bits are set to zero.
	//    AppendTextBuffer("bTriggerSupport:                   0x%02X", VidInHeaderDesc->bTriggerSupport);

	//@@TestCase B11.9
	//@@Not yet implemented - Priority 1
	//@@Descriptor Field - bTriggerUsage
	//@@We should validate that reserved bits are set to zero.
	//    AppendTextBuffer("bTriggerUsage:                     0x%02X", VidInHeaderDesc->bTriggerUsage);

	return TRUE;
}


//*****************************************************************************
//
// DisplayVidOutHeader()
//
//*****************************************************************************

BOOL
DisplayVidOutHeader(
	PVIDEO_STREAMING_OUTPUT_HEADER VidOutHeaderDesc
)
{
	//@@DisplayVidOutHeader -Video Streaming Video Output Header
	UINT  uCount = 0;
	UCHAR bLength = sizeof(VIDEO_STREAMING_OUTPUT_HEADER);

	AppendTextBuffer("\r\n          ===>Video Class-Specific VS Video Output Header Descriptor<===\r\n");
	AppendTextBuffer("bLength:                           0x%02X\r\n", VidOutHeaderDesc->bLength);
	AppendTextBuffer("bDescriptorType:                   0x%02X\r\n", VidOutHeaderDesc->bDescriptorType);
	AppendTextBuffer("bDescriptorSubtype:                0x%02X\r\n", VidOutHeaderDesc->bDescriptorSubtype);
	AppendTextBuffer("bNumFormats:                       0x%02X\r\n", VidOutHeaderDesc->bNumFormats);
	AppendTextBuffer("wTotalLength:                    0x%04X", VidOutHeaderDesc->wTotalLength);

	uCount = GetVSInterfaceSize((PUSB_COMMON_DESCRIPTOR)VidOutHeaderDesc, VidOutHeaderDesc->wTotalLength);
	if (uCount != VidOutHeaderDesc->wTotalLength) {
		AppendTextBuffer("\r\n*!*ERROR:  invalid interface size 0x%02X, should be 0x%02X\r\n",
			VidOutHeaderDesc->wTotalLength, uCount);
	}
	else {
		AppendTextBuffer("  -> Validated\r\n");
	}

	AppendTextBuffer("bEndpointAddress:                  0x%02X", VidOutHeaderDesc->bEndpointAddress);
	if (USB_ENDPOINT_DIRECTION_OUT(VidOutHeaderDesc->bEndpointAddress)) {
		if (gDoAnnotation)
		{
			AppendTextBuffer("  -> Direction: OUT - EndpointID: %d",
				(VidOutHeaderDesc->bEndpointAddress & 0x0F));
		}
		AppendTextBuffer("\r\n");
	}
	AppendTextBuffer("bTerminalLink:                     0x%02X\r\n", VidOutHeaderDesc->bTerminalLink);

	// UVC11 Video Output Header has additional fields, larger size
#ifdef H264_SUPPORT
	if (UVC10 != g_chUVCversion)
#else
	if (UVC11 == g_chUVCversion)
#endif
	{
		UCHAR   bControlSize = 0;
		PUCHAR  pControls = NULL;

		// bControlSize field is next after bTerminalLink
		pControls = &(VidOutHeaderDesc->bTerminalLink) + 1;
		bControlSize = *(pControls);
		// point to first bmaControls
		pControls++;

		// Size of UVC 1.1 Video Output Header is 1.0 size 
		//  plus 1 (bControlSize field) plus (number of formats * bControlSize)
		bLength += 1 + (VidOutHeaderDesc->bNumFormats * bControlSize);

		// Need new uvcdesc.h to handle new fields
		AppendTextBuffer("bControlSize:                      0x%02X\r\n", bControlSize);

		// are there formats to display?
		if (VidOutHeaderDesc->bNumFormats)
		{
			UINT   uFormatIndex = 1;
			UINT   uBitIndex = 0;
			BYTE   cCheckBit = 0;
			BYTE   cMask = 1;

			// There are (bNumFormats) bmaControls fields, each with size (bControlSize)
			for (; uFormatIndex <= VidOutHeaderDesc->bNumFormats; uFormatIndex++, pControls++)
			{
				AppendTextBuffer("Video Payload Format %d             ", uFormatIndex);

				// Handle case of 0 control size
				if (0 == bControlSize)
				{
					AppendTextBuffer("0x00\r\n");
				}
				else
				{
					VDisplayBytes(pControls, bControlSize);

					// map the first control    
					for (uBitIndex = 0, cMask = 1; uBitIndex < 8; uBitIndex++)
					{
						cCheckBit = cMask & *(pControls);

						AppendTextBuffer("     D%02d = %d  %s %s\r\n",
							uBitIndex,
							cCheckBit ? 1 : 0,
							cCheckBit ? "yes - " : " no - ",
							GetStringFromList(slOutputHeaderControls,
								sizeof(slOutputHeaderControls) / sizeof(STRINGLIST),
								cMask,
								"Invalid control value"));

						cMask = cMask << 1;
					}
				}
			} // for ( pData++ ; uFormatIndex <= VidOutHeaderDesc->bNumFormats; uFormatIndex++ )
		} // if (VidOutHeaderDesc->bNumFormats)
	} // if (UVC11 == g_chUVCversion)

	if (VidOutHeaderDesc->bLength != bLength)
	{
		//@@TestCase B12.1  (also in Descript.c)
		//@@ERROR
		//@@Descriptor Field - bLength
		//@@The declared length in the device descriptor is not equal to the 
		//@@  required length in the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  bLength of %d incorrect, should be %d\r\n",
			VidOutHeaderDesc->bLength,
			sizeof(VIDEO_STREAMING_OUTPUT_HEADER));
		OOPS();
	}

	if (VidOutHeaderDesc->bNumFormats < 1)
	{
		//@@TestCase B12.2 (Descript.c  Line 827)
		//@@ERROR
		//@@Descriptor Field - bNumFormats
		//@@bNumFormats should be greater than 0
		//@@Question: Should we test to verify the non-zero value for bNumFormats is valid
		AppendTextBuffer("*!*ERROR:  bNumFormats of %d is too small\r\n",
			VidOutHeaderDesc->bNumFormats);
		OOPS();
	}

	if (VidOutHeaderDesc->wTotalLength < VidOutHeaderDesc->bLength)
	{
		//@@TestCase B12.3 (Descript.c  Line 832)
		//@@ERROR
		//@@Descriptor Field - wTotalLength
		//@@wTotalLength should be greater than bLength
		//@@Question: Should we calculate wTotalLength to verify the value is valid
		AppendTextBuffer("*!*ERROR:  wTotalLength of %d is small than the bLength of %d\r\n",
			VidOutHeaderDesc->wTotalLength,
			VidOutHeaderDesc->bLength);
		OOPS();
	}

	if (VidOutHeaderDesc->bEndpointAddress < 1)
	{
		//@@TestCase B12.4  (Descript.c  Line 837)
		//@@ERROR
		//@@Descriptor Field - bEndpointAddress
		//@@bEndpointAddress should be greater than 0
		//@@Question: Should we test to verify the non-zero value for bEndpointAddress is valid
		AppendTextBuffer("*!*ERROR:  bEndpointAddress of %d is too small\r\n",
			VidOutHeaderDesc->bEndpointAddress);
		OOPS();
	}

	if (!(USB_ENDPOINT_DIRECTION_OUT(VidOutHeaderDesc->bEndpointAddress))) {
		//@@TestCase B12.5
		//@@ERROR
		//@@Descriptor Field - bEndPointAddress
		//@@The bEndPointAddress is set for the wrong direction
		AppendTextBuffer("\r\n*!*ERROR:  bEndPointAddress needs to have the Direction OUT for this header\r\n");
		OOPS();
	}

	if (VidOutHeaderDesc->bTerminalLink < 1)
	{
		//@@TestCase B12.6 (Descript.c  Line 842)
		//@@ERROR
		//@@Descriptor Field - bTerminalLink
		//@@bTerminalLink should be greater than 0
		//@@Question: Should we test to verify the non-zero value for bTerminalLink is valid
		AppendTextBuffer("*!*ERROR:  bTerminalLink of %d is too small\r\n",
			VidOutHeaderDesc->bTerminalLink);
		OOPS();
	}

	return TRUE;

}


//*****************************************************************************
//
// DisplayStillImageFrame()
//
//*****************************************************************************

BOOL
DisplayStillImageFrame(
	PVIDEO_STILL_IMAGE_FRAME StillFrameDesc
)
{
	//@@DisplayStillImageFrame -Still Image Frame
	VIDEO_STILL_IMAGE_RECT* pXY;
	PUCHAR      pbCurr = NULL;
	UINT        i = 0;
	UINT        uNumComp = 0;
	UINT        uSize = 0;
	size_t      bLength = 0;

	bLength = SizeOfVideoStillImageFrame(StillFrameDesc);

	AppendTextBuffer("\r\n          ===>Still Image Frame Type Descriptor<===\r\n");
	AppendTextBuffer("bLength:                           0x%02X\r\n", StillFrameDesc->bLength);
	AppendTextBuffer("bDescriptorType:                   0x%02X\r\n", StillFrameDesc->bDescriptorType);
	AppendTextBuffer("bDescriptorSubtype:                0x%02X\r\n", StillFrameDesc->bDescriptorSubtype);
	AppendTextBuffer("bEndpointAddress:                  0x%02X\r\n", StillFrameDesc->bEndpointAddress);
	AppendTextBuffer("bNumImageSizePatterns:             0x%02X\r\n",
		StillFrameDesc->bNumImageSizePatterns);
	if (StillFrameDesc->bNumImageSizePatterns < 1)
	{
		//@@TestCase B13.1 (also Descript.c Line 886)
		//@@Not yet implemented - Priority 1
		//@@Descriptor Field - bNumImageSizePatterns
		//@@The bNumImageSizePatterns should be greater than 0
		//@@Question: Should we test to verify the non-zero value for bNumImageSizePatterns is valid
		AppendTextBuffer("*!*ERROR:  bNumImageSizePatterns must be non-zero\r\n");
		OOPS();
	}

	// point to first StillFrameDesc->dwStillImage structure
	pXY = (VIDEO_STILL_IMAGE_RECT*)&StillFrameDesc->aStillRect[0];

	for (i = 1; i <= StillFrameDesc->bNumImageSizePatterns; i++, pXY++)
	{
		AppendTextBuffer("wWidth[%d]:                       0x%04X\r\n",
			i, pXY->wWidth);
		AppendTextBuffer("wHeight[%d]:                      0x%04X\r\n",
			i, pXY->wHeight);
	}
	// point to bNumCompressionPattern field (after variable count field dwStillImage)
	pbCurr = (PUCHAR)pXY;
	// get number of compression patterns
	uNumComp = *pbCurr;

	AppendTextBuffer("bNumCompressionPattern:            0x%02X\r\n", *pbCurr++);
	for (i = 1; i <= uNumComp; i++)
	{
		AppendTextBuffer("bCompression[%d]:                   0x%02X\r\n",
			i, *pbCurr++);
	}

	switch (StillMethod) {
	case 0:
		//@@TestCase B13.2
		//@@ERROR
		//@@Descriptor Field - Still Image Frame Type Descriptor
		//@@An still method type has been defined that shouldn't use a Still Image Frame
		AppendTextBuffer("*!*ERROR:  VS Video Input Header set to "\
			"No Still Method support\r\n");
		OOPS();
	case 1:
		//@@TestCase B13.3
		//@@ERROR
		//@@Descriptor Field - Still Image Frame Type Descriptor
		//@@An still method type has been defined that shouldn't use a Still Image Frame
		AppendTextBuffer("*!*ERROR:  VS Video Input Header set to "\
			"Still Method One support with a Still Image Frame descriptor\r\n");
		OOPS();
	default:
		break;
	}

	if (StillFrameDesc->bLength != bLength)
	{
		//@@TestCase B13.4 (Also in descript.c)
		//@@ERROR
		//@@Descriptor Field - bLength
		//@@The declared length in the device descriptor is incorrect
		AppendTextBuffer("*!*ERROR:  bLength 0x%02X incorrect, should be 0x%02X\r\n",
			StillFrameDesc->bLength, uSize);
		OOPS();
	}

	//@@TestCase B13.5
	//@@Not yet implemented - Priority 1
	//@@Descriptor Field - bEndpointAddress
	//@@Should test to verify endpoint validity
	//    AppendTextBuffer("bEndpointAddress:                  0x%02X", StillFrameDesc->bEndpointAddress);

	if (USB_ENDPOINT_DIRECTION_IN(StillFrameDesc->bEndpointAddress) && StillMethod == 3) {
		if ((StillFrameDesc->bEndpointAddress) == 0) {
			//@@TestCase B13.6
			//@@ERROR
			//@@Descriptor Field - bEndPointAddress
			//@@bEndPointAddress should be non-zero for 0 when using StillMethod 3
			AppendTextBuffer("\r\n*!*ERROR:  bEndpointAddress is reported as %d.  "\
				"This should be non-zero when using StillMethod 3.\r\n",
				(StillFrameDesc->bEndpointAddress));
			OOPS();
		}
		if (gDoAnnotation)
		{
			AppendTextBuffer("  -> Direction: IN - EndpointID: %d",
				(StillFrameDesc->bEndpointAddress & 0x0F));
		}
		AppendTextBuffer("\r\n");
	}
	else if (USB_ENDPOINT_DIRECTION_OUT(StillFrameDesc->bEndpointAddress) && StillMethod == 2) {
		if ((StillFrameDesc->bEndpointAddress & 0x0F) != 0) {
			//@@TestCase B13.7
			//@@ERROR
			//@@Descriptor Field - bEndPointAddress
			//@@The EndpointID of bEndPointAddress should be set for 0 when using StillMethod 2
			AppendTextBuffer("\r\n*!*ERROR:  The EndpointID of the "\
				"bEndpointAddress is reported as %d.  This should be 0.\r\n",
				(StillFrameDesc->bEndpointAddress & 0x0F));
			OOPS();
		}
		else { AppendTextBuffer("\r\n"); }
	}
	else if (StillFrameDesc->bEndpointAddress != 0) {
		//@@TestCase B13.8
		//@@ERROR
		//@@Descriptor Field - bEndPointAddress
		//@@The bEndPointAddress should be set for 0 when not using StillMethod 2 or 3
		AppendTextBuffer("\r\n*!*ERROR:  bEndPointAddress should be 0.\r\n");
		OOPS();
	}
	else { AppendTextBuffer("\r\n"); }

	return TRUE;
}


//*****************************************************************************
//
// DisplayColorMatching()
//
//*****************************************************************************

BOOL
DisplayColorMatching(
	PVIDEO_COLORFORMAT ColorMatchDesc
)
{
	//@@DisplayColorMatching -Color Matching

	AppendTextBuffer("\r\n          ===>Color Matching Descriptor<===\r\n");
	AppendTextBuffer("bLength:                           0x%02X\r\n", ColorMatchDesc->bLength);
	AppendTextBuffer("bDescriptorType:                   0x%02X\r\n", ColorMatchDesc->bDescriptorType);
	AppendTextBuffer("bDescriptorSubtype:                0x%02X\r\n", ColorMatchDesc->bDescriptorSubtype);
	AppendTextBuffer("bColorPrimaries:                   0x%02X\r\n", ColorMatchDesc->bColorPrimaries);
	AppendTextBuffer("bTransferCharacteristics:          0x%02X\r\n", ColorMatchDesc->bTransferCharacteristics);
	AppendTextBuffer("bMatrixCoefficients:               0x%02X\r\n", ColorMatchDesc->bMatrixCoefficients);

	if (ColorMatchDesc->bLength != sizeof(VIDEO_COLORFORMAT))
	{
		//@@TestCase B14.1 (Descript.c Line 1596)
		//@@ERROR
		//@@Descriptor Field - bLength
		//@@The declared length in the device descriptor is not equal to the required length in the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  bLength of %d incorrect, should be %d\r\n",
			ColorMatchDesc->bLength,
			sizeof(VIDEO_COLORFORMAT));
		OOPS();
	}

	//@@TestCase B14.2
	//@@Not yet implemented - Priority 1
	//@@Descriptor Field - bColorPrimaries
	//@@Question - Should we test to verify bColorPrimaries
	//    AppendTextBuffer("bColorPrimaries:                   0x%02X\r\n", ColorMatchDesc->bColorPrimaries);

	//@@TestCase B14.3
	//@@Not yet implemented - Priority 1
	//@@Descriptor Field - bTransferCharacteristics
	//@@Question - Should we test to verify bTransferCharacteristics
	//    AppendTextBuffer("bTransferCharacteristics:          0x%02X\r\n", ColorMatchDesc->bTransferCharacteristics);

	//@@TestCase B14.4
	//@@Not yet implemented - Priority 1
	//@@Descriptor Field - bMatrixCoefficients
	//@@Question - Should we test to verify bMatrixCoefficients
	//    AppendTextBuffer("bMatrixCoefficients:               0x%02X\r\n", ColorMatchDesc->bMatrixCoefficients);

	return TRUE;
}


//*****************************************************************************
//
// DisplayUncompressedFormat() 
//
//*****************************************************************************

BOOL
DisplayUncompressedFormat(
	PVIDEO_FORMAT_UNCOMPRESSED UnCompFormatDesc
)
{
	//@@DisplayUncompressedFormat - Uncompressed Format
	int i = 0;
	PCHAR pStr = NULL;
	OLECHAR szGUID[256];

	// Initialize the default Frame
	g_chUNCFrameDefault = UnCompFormatDesc->bDefaultFrameIndex;

	memset((LPOLESTR)szGUID, 0, sizeof(OLECHAR) * 256);
	i = StringFromGUID2((REFGUID)&UnCompFormatDesc->guidFormat, (LPOLESTR)szGUID, 255);
	i++;

	AppendTextBuffer("\r\n          ===>Video Streaming Uncompressed Format Type Descriptor<===\r\n");
	AppendTextBuffer("bLength:                           0x%02X\r\n", UnCompFormatDesc->bLength);
	AppendTextBuffer("bDescriptorType:                   0x%02X\r\n", UnCompFormatDesc->bDescriptorType);
	AppendTextBuffer("bDescriptorSubtype:                0x%02X\r\n", UnCompFormatDesc->bDescriptorSubtype);
	AppendTextBuffer("bFormatIndex:                      0x%02X\r\n", UnCompFormatDesc->bFormatIndex);
	AppendTextBuffer("bNumFrameDescriptors:              0x%02X\r\n", UnCompFormatDesc->bNumFrameDescriptors);
	AppendTextBuffer("guidFormat:                        %S", szGUID);

	pStr = VidFormatGUIDCodeToName((REFGUID)&UnCompFormatDesc->guidFormat);
	if (pStr)
	{
		if (gDoAnnotation)
		{
			AppendTextBuffer(" = %s Format", pStr);
		}
	}
	AppendTextBuffer("\r\n");
	AppendTextBuffer("bBitsPerPixel:                     0x%02X\r\n", UnCompFormatDesc->bBitsPerPixel);
	AppendTextBuffer("bDefaultFrameIndex:                0x%02X\r\n", UnCompFormatDesc->bDefaultFrameIndex);

	if (UnCompFormatDesc->bLength != sizeof(VIDEO_FORMAT_UNCOMPRESSED))
	{
		//@@TestCase B15.1 (descript.c line 925)
		//@@ERROR
		//@@Descriptor Field - bLength
		//@@The declared length in the device descriptor is not equal to the required 
		//@@length in the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  bLength of %d incorrect, should be %d\r\n",
			UnCompFormatDesc->bLength,
			sizeof(VIDEO_FORMAT_UNCOMPRESSED));
		OOPS();
	}

	if (UnCompFormatDesc->bFormatIndex == 0)
	{
		//@@TestCase B15.2 (descript.c line 930)
		//@@ERROR
		//@@Descriptor Field - bFormatIndex
		//@@bFormatIndex is set to zero which is not in accordance with the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  bFormatIndex = 0, this is a 1 based index\r\n");
		OOPS();
	}

	if (UnCompFormatDesc->bNumFrameDescriptors == 0)
	{
		//@@TestCase B15.3 (descript.c line 930)
		//@@ERROR
		//@@Descriptor Field - bNumFrameDescriptors
		//@@bNumFrameDescriptors is set to zero which is not in accordance with the 
		//@@USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  bNumFrameDescriptors = 0, must have at least 1 Frame descriptor\r\n");
		OOPS();
	}

	if (!(pStr))
	{
		//@@TestCase B15.4
		//@@WARNING
		//@@Descriptor Field - guidFormat
		//@@guidFormat is set to unknown or undefined format
		AppendTextBuffer("\r\n*!*WARNING:  guidFormat is an unknown format\r\n");
		OOPS();
	}

	if (UnCompFormatDesc->bBitsPerPixel == 0)
	{
		//@@TestCase B15.5 (descript.c line 940)
		//@@ERROR
		//@@Descriptor Field - bBitsPerPixel
		//@@bBitsPerPixel is set to zero which is not in accordance with the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  bBitsPerPixel = 0, this invalidates the descriptor\r\n");
		OOPS();
	}

	if (UnCompFormatDesc->bDefaultFrameIndex == 0 || UnCompFormatDesc->bDefaultFrameIndex >
		UnCompFormatDesc->bNumFrameDescriptors)
	{
		//@@TestCase B15.6 (desctipt.c line 945)
		//@@ERROR
		//@@Descriptor Field - bDefaultFrameIndex
		//@@The value for bDefaultFrameIndex is not greater than 0 or less than or equal to bNumFrameDescriptors
		AppendTextBuffer("*!*ERROR:  The value %d for the bDefaultFrameIndex is out of range, this invalidates the descriptor\r\n*!*The proper range is 1 to %d)",
			UnCompFormatDesc->bDefaultFrameIndex,
			UnCompFormatDesc->bNumFrameDescriptors);
		OOPS();
	}

	AppendTextBuffer("bAspectRatioX:                     0x%02X\r\n",
		UnCompFormatDesc->bAspectRatioX);
	AppendTextBuffer("bAspectRatioY:                     0x%02X",
		UnCompFormatDesc->bAspectRatioY);

	if (((UnCompFormatDesc->bmInterlaceFlags & 0x01) &&
		(UnCompFormatDesc->bAspectRatioY != 0 &&
			UnCompFormatDesc->bAspectRatioX != 0)))
	{
		if (gDoAnnotation)
		{
			AppendTextBuffer("  -> Aspect Ratio is set for a %d:%d display",
				(UnCompFormatDesc->bAspectRatioX), (UnCompFormatDesc->bAspectRatioY));
		}
		else
		{
			if (UnCompFormatDesc->bAspectRatioY != 0 || UnCompFormatDesc->bAspectRatioX != 0)
			{
				//@@TestCase B15.7
				//@@ERROR
				//@@Descriptor Field - bAspectRatioX, bAspectRatioY
				//@@Verify that that bAspectRatioX and bAspectRatioY are  set to zero 
				//@@  if stream is non-interlaced
				AppendTextBuffer("\r\n*!*ERROR:  Both bAspectRatioX and bAspectRatioY "\
					"must equal 0 if stream is non-interlaced");
				OOPS();
			}
		}
	}
	AppendTextBuffer("\r\nbmInterlaceFlags:                  0x%02X\r\n",
		UnCompFormatDesc->bmInterlaceFlags);

	if (gDoAnnotation)
	{
		AppendTextBuffer("     D0    = 0x%02X Interlaced stream or variable: %s\r\n",
			(UnCompFormatDesc->bmInterlaceFlags & 1),
			(UnCompFormatDesc->bmInterlaceFlags & 1) ? "Yes" : "No");
		AppendTextBuffer("     D1    = 0x%02X Fields per frame: %s\r\n",
			((UnCompFormatDesc->bmInterlaceFlags >> 1) & 1),
			((UnCompFormatDesc->bmInterlaceFlags >> 1) & 1) ? "1 field" : "2 fields");
		AppendTextBuffer("     D2    = 0x%02X Field 1 first: %s\r\n",
			((UnCompFormatDesc->bmInterlaceFlags >> 2) & 1),
			((UnCompFormatDesc->bmInterlaceFlags >> 2) & 1) ? "Yes" : "No");
		//@@TestCase B15.9
		//@@Not yet implemented - Priority 1
		//@@Descriptor Field - bmInterlaceFlags
		//@@Validate that reserved bits (D3) are set to zero.
		AppendTextBuffer("     D3    = 0x%02X Reserved%s\r\n",
			((UnCompFormatDesc->bmInterlaceFlags >> 3) & 1),
			((UnCompFormatDesc->bmInterlaceFlags >> 3) & 1) ?
			"\r\n*!*ERROR: Reserved to 0" : "");
		AppendTextBuffer("     D4..5 = 0x%02X Field patterns  ->",
			((UnCompFormatDesc->bmInterlaceFlags >> 4) & 3));
		switch (UnCompFormatDesc->bmInterlaceFlags & 0x30)
		{
		case 0x00:
			AppendTextBuffer(" Field 1 only");
			break;
		case 0x10:
			AppendTextBuffer(" Field 2 only");
			break;
		case 0x20:
			AppendTextBuffer(" Regular Pattern of fields 1 and 2");
			break;
		case 0x30:
			AppendTextBuffer(" Random Pattern of fields 1 and 2");
			break;
		}
		AppendTextBuffer("\r\n     D6..7 = 0x%02X Display Mode  ->",
			((UnCompFormatDesc->bmInterlaceFlags >> 6) & 3));

		switch (UnCompFormatDesc->bmInterlaceFlags & 0xC0)
		{
		case 0x00:
			AppendTextBuffer(" Bob only");
			break;
		case 0x40:
			AppendTextBuffer(" Weave only");
			break;
		case 0x80:
			AppendTextBuffer(" Bob or weave");
			break;
		case 0xC0:
			//@@TestCase B15.10
			//@@Not yet implemented - Priority 3
			//@@Descriptor Field - bmInterlaceFlags
			//@@Question - Should we validate that reserved bits are set to zero?
			AppendTextBuffer(" Reserved");
			break;
		}
	}

	//@@TestCase B15.11
	//@@Not yet implemented - Priority 1
	//@@Descriptor Field - bCopyProtect
	//@@Question - Are their reserved bits and should we validate that 
	//@@  reserved bits are set to zero?
	AppendTextBuffer("\r\nbCopyProtect:                      0x%02X",
		UnCompFormatDesc->bCopyProtect);
	if (gDoAnnotation)
	{
		if (UnCompFormatDesc->bCopyProtect)
			AppendTextBuffer("  -> Duplication Restricted");
		else
			AppendTextBuffer("  -> Duplication Unrestricted");
	}
	AppendTextBuffer("\r\n");

	//@@TestCase B15.12
	//@@We should check to make sure that a Color Matching Descriptor is included in the device
	// Check that the correct number of Frame Descriptors and one Color Matching
	//  descriptor follow
	CheckForColorMatchingDesc((PVIDEO_SPECIFIC)UnCompFormatDesc,
		UnCompFormatDesc->bNumFrameDescriptors, VS_FRAME_UNCOMPRESSED);

	return TRUE;
}


//*****************************************************************************
//
// DisplayUncompressedFrameType()
//
//*****************************************************************************

BOOL
DisplayUncompressedFrameType(
	PVIDEO_FRAME_UNCOMPRESSED UnCompFrameDesc
)
{
	size_t bLength = 0;
	bLength = SizeOfVideoFrameUncompressed(UnCompFrameDesc);

	//@@DisplayUncompressedFrameType -Uncompressed Frame

	AppendTextBuffer("\r\n          ===>Video Streaming Uncompressed Frame Type Descriptor<===\r\n");
	if (gDoAnnotation)
	{
		if (UnCompFrameDesc->bFrameIndex == g_chUNCFrameDefault)
		{
			AppendTextBuffer("          --->This is the Default (optimum) Frame index\r\n");
		}
	}
	AppendTextBuffer("bLength:                           0x%02X\r\n", UnCompFrameDesc->bLength);
	AppendTextBuffer("bDescriptorType:                   0x%02X\r\n", UnCompFrameDesc->bDescriptorType);
	AppendTextBuffer("bDescriptorSubtype:                0x%02X\r\n", UnCompFrameDesc->bDescriptorSubtype);
	AppendTextBuffer("bFrameIndex:                       0x%02X\r\n", UnCompFrameDesc->bFrameIndex);
	AppendTextBuffer("bmCapabilities:                    0x%02X\r\n", UnCompFrameDesc->bmCapabilities);
	AppendTextBuffer("wWidth:                          0x%04X = %d\r\n", UnCompFrameDesc->wWidth, UnCompFrameDesc->wWidth);
	AppendTextBuffer("wHeight:                         0x%04X = %d\r\n", UnCompFrameDesc->wHeight, UnCompFrameDesc->wHeight);
	AppendTextBuffer("dwMinBitRate:                0x%08X\r\n", UnCompFrameDesc->dwMinBitRate);
	AppendTextBuffer("dwMaxBitRate:                0x%08X\r\n", UnCompFrameDesc->dwMaxBitRate);
	AppendTextBuffer("dwMaxVideoFrameBufferSize:   0x%08X\r\n", UnCompFrameDesc->dwMaxVideoFrameBufferSize);
	// To convert the default frame interval, which is in 100 ns units,  to  milliseconds, we divide by 10,000.
	// 100 ns = 10^(-7) seconds = 10^(-7) sec * 1000 msec/sec = 10^(-7) * 10^3 milleseconds = 10^(-4) seconds
	// = 1/10,000 milliseconds


	// To convert the frame interval to Hz, we divide by 10,000,000 and then take the inverse

	AppendTextBuffer("dwDefaultFrameInterval:      0x%08X = %lf mSec (%4.2f Hz)\r\n",
		UnCompFrameDesc->dwDefaultFrameInterval,
		((double)UnCompFrameDesc->dwDefaultFrameInterval) / 10000.0,
		(10000000.0 / ((double)UnCompFrameDesc->dwDefaultFrameInterval))
	);
	AppendTextBuffer("bFrameIntervalType:                0x%02X\r\n", UnCompFrameDesc->bFrameIntervalType);

	if (UnCompFrameDesc->bLength != bLength)
	{
		//@@TestCase B15.1 (descript.c line 925)
		//@@ERROR
		//@@Descriptor Field - bLength
		//@@The declared length in the device descriptor is not equal to the required 
		//@@length in the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  bLength of %d incorrect, should be %d\r\n",
			UnCompFrameDesc->bLength, bLength);
		OOPS();
	}

	if (UnCompFrameDesc->bFrameIndex == 0)
	{
		//@@TestCase B16.2 (descript.c line 991)
		//@@ERROR
		//@@Descriptor Field - bFrameIndex
		//@@bFrameIndex must be nonzero 
		AppendTextBuffer("*!*ERROR:  bFrameIndex = 0, this is a 1 based index\r\n");
		OOPS();
	}

	//@@TestCase B16.3
	//@@Not yet implemented - Priority 1
	//@@Descriptor Field - bmCapabilities
	//@@Question:  Should we try to verify that bmCapabilities is valid?
	//    AppendTextBuffer("bmCapabilities:                    0x%02X\r\n", UnCompFrameDesc->bmCapabilities);

	if (UnCompFrameDesc->wWidth == 0)
	{
		//@@TestCase B16.4 (descript.c line 996)
		//@@ERROR
		//@@Descriptor Field - wWidth
		//@@wWidth must be nonzero
		AppendTextBuffer("*!*ERROR:  wWidth must be nonzero\r\n");
		OOPS();
	}

	if (UnCompFrameDesc->wHeight == 0)
	{
		//@@TestCase B16.5 (descript.c line 1001)
		//@@ERROR
		//@@Descriptor Field - wHeight
		//@@wHeight must be nonzero
		AppendTextBuffer("*!*ERROR:  wHeight must be nonzero\r\n");
		OOPS();
	}

	if (UnCompFrameDesc->dwMinBitRate == 0)
	{
		//@@TestCase B16.6 (descript.c line 1006)
		//@@ERROR
		//@@Descriptor Field - dwMinBitRate
		//@@dwMinBitRate must be nonzero
		AppendTextBuffer("*!*ERROR:  dwMinBitRate must be nonzero\r\n");
		OOPS();
	}

	if (UnCompFrameDesc->dwMaxBitRate == 0)
	{
		//@@TestCase B16.7 (descript.c line 1011)
		//@@ERROR
		//@@Descriptor Field - dwMaxBitRate
		//@@dwMaxBitRate must be nonzero
		AppendTextBuffer("*!*ERROR:  dwMaxBitRate must be nonzero\r\n");
		OOPS();
	}

	if (UnCompFrameDesc->dwMinBitRate > UnCompFrameDesc->dwMaxBitRate)
	{
		//@@TestCase B16.8
		//@@ERROR
		//@@Descriptor Field - dwMinBitRate and dwMaxBitRate
		//@@Verify that dwMaxBitRate is greater than dwMinBitRate
		AppendTextBuffer("*!*ERROR:  dwMinBitRate should be less than dwMaxBitRate\r\n");
		OOPS();
	}
	else
	{
		if (UnCompFrameDesc->bFrameIntervalType == 1 &&
			UnCompFrameDesc->dwMinBitRate != UnCompFrameDesc->dwMaxBitRate)
		{
			//@@TestCase B16.9
			//@@WARNING
			//@@Descriptor Field - bFrameIntervalType, dwMinBitRate, and dwMaxBitRate
			//@@Verify that dwMaxBitRate is equal to dwMinBitRate if bFrameIntervalType is 1
			AppendTextBuffer("*!*WARNING:  if bFrameIntervalType is 1 then dwMinBitRate "\
				"should equal dwMaxBitRate\r\n");
			OOPS();
		}
	}

	if (UnCompFrameDesc->dwMaxVideoFrameBufferSize == 0)
	{
		//@@TestCase B16.10 (descript.c line 1015)
		//@@WARNING
		//@@Descriptor Field - bFrameIndex
		//@@bFrameIndex must be nonzero
		AppendTextBuffer("*!*WARNING:  dwMaxVideoFrameBufferSize must be nonzero\r\n");
		OOPS();
	}

	if (UnCompFrameDesc->dwDefaultFrameInterval == 0)
	{
		//@@TestCase B16.11 (descript.c line 1020)
		//@@WARNING
		//@@Descriptor Field - dwDefaultFrameInterval
		//@@dwDefaultFrameInterval must be nonzero
		AppendTextBuffer("*!*WARNING:  dwDefaultFrameInterval must be nonzero\r\n");
		OOPS();
	}
	if (0 == UnCompFrameDesc->bFrameIntervalType)
	{
		DisplayUnComContinuousFrameType(UnCompFrameDesc);
	}
	else
	{
		DisplayUnComDiscreteFrameType(UnCompFrameDesc);
	}
	return TRUE;
}

//*****************************************************************************
//
// DisplayUnComContinuousFrameType()
//
//*****************************************************************************

BOOL
DisplayUnComContinuousFrameType(
	PVIDEO_FRAME_UNCOMPRESSED UContinuousDesc
)
{
	//@@DisplayUnComContinuousFrameType -Uncompressed Continuous Frame
	ULONG dwMinFrameInterval = UContinuousDesc->adwFrameInterval[0];
	ULONG dwMaxFrameInterval = UContinuousDesc->adwFrameInterval[1];
	ULONG dwFrameIntervalStep = UContinuousDesc->adwFrameInterval[2];

	AppendTextBuffer("===>Additional Continuous Frame Type Data\r\n");
	// To convert the default frame interval, which is in 100 ns units,  to  milliseconds, we divide by 10,000.
	// 100 ns = 10^(-7) seconds = 10^(-7) sec * 1000 msec/sec = 10^(-7) * 10^3 milleseconds = 10^(-4) seconds
	// = 1/10,000 milliseconds


	// To convert the frame interval to Hz, we divide by 10,000,000 and then take the inverse


	AppendTextBuffer("dwMinFrameInterval:          0x%08X = %lf mSec (%d Hz)\r\n",
		dwMinFrameInterval,
		((double)dwMinFrameInterval) / 10000.0,
		(ULONG)(10000000.0 / ((double)dwMinFrameInterval) + 0.5));

	AppendTextBuffer("dwMaxFrameInterval:          0x%08X = %lf mSec (%d Hz)\r\n",
		dwMaxFrameInterval,
		((double)dwMaxFrameInterval) / 10000.0,
		(ULONG)(10000000.0 / ((double)dwMaxFrameInterval) + 0.5));

	AppendTextBuffer("dwFrameIntervalStep:         0x%08X\r\n", dwFrameIntervalStep);

	if (dwMinFrameInterval == 0)
	{
		//@@TestCase B17.2 (descript.c line 1025)
		//@@ERROR
		//@@Descriptor Field - dwMinFrameInterval
		//@@dwMinFrameInterval is set to zero which is not in accordance with the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  dwMinFrameInterval = 0, this invalidates the descriptor\r\n");
		OOPS();
	}

	if (dwMaxFrameInterval == 0)
	{
		//@@TestCase B17.3 (descript.c line 1025)
		//@@ERROR
		//@@Descriptor Field - dwMaxFrameInterval
		//@@dwMaxFrameInterval is set to zero which is not in accordance with the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  dwMaxFrameInterval = 0, this invalidates the descriptor\r\n");
		OOPS();
	}

	if (dwMinFrameInterval > dwMaxFrameInterval)
	{
		//@@TestCase B17.4  (descript.c 1043)
		//@@ERROR
		//@@Descriptor Field - dwMinFrameInterval and dwMaxFrameInterval
		//@@Verify that dwMaxFrameInterval is greater than dwMinFrameInterval
		AppendTextBuffer("*!*ERROR:  dwMinFrameInterval is larger that dwMaxFrameInterval, this invalidates the descriptor\r\n");
		OOPS();
	}
	else if ((dwMinFrameInterval + dwFrameIntervalStep) > dwMaxFrameInterval)
	{
		//@@TestCase B17.5
		//@@WARNING
		//@@Descriptor Field - dwFrameIntervalStep, dwMinFrameInterval, and dwMaxFrameInterval
		//@@Verify that dwMaxFrameInterval is greater than dwMinFrameInterval combined with dwFrameIntervalStep
		AppendTextBuffer("*!*WARNING:  dwMinFrameInterval + dwFrameIntervalStep is greater than dwMaxFrameInterval, this could cause problems\r\n");
		OOPS();
	}
	else if ((dwMaxFrameInterval - dwMinFrameInterval) == 0)
	{
		//@@TestCase B17.6
		//@@CAUTION
		//@@Descriptor Field - dwFrameIntervalStep
		//@@Suggestion to use descrite frames if dwFrameIntervalStep is zero
		AppendTextBuffer("*!*CAUTION:  dwFrameIntervalStep equals zero, consider using discrete frames\r\n");
		OOPS();
	}
	else if ((dwMaxFrameInterval - dwMinFrameInterval) % dwFrameIntervalStep)
	{
		//@@TestCase B17.7 (descript.c 1052)
		//@@WARNING
		//@@Descriptor Field - dwFrameIntervalStep, dwMinFrameInterval, and dwMaxFrameInterval
		//@@Verify that the difference between dwMaxFrameInterval and dwMinFrameInterval is evenly divisible by dwFrameIntervalStep
		AppendTextBuffer("*!*WARNING:  dwMaxFrameInterval minus dwMinFrameInterval  is not evenly divisible by dwFrameIntervalStep, this could cause problems\r\n");
		OOPS();
	}

	if (dwFrameIntervalStep == 0 && (dwMaxFrameInterval - dwMinFrameInterval))
	{
		//@@TestCase B17.8 (descript.c line 1032)
		//@@WARNING
		//@@Descriptor Field - dwFrameIntervalStep, dwMinFrameInterval, and dwMaxFrameInterval
		//@@Verify that the dwFrameIntervalStep is not zero if there is a difference between dwMaxFrameInterval and dwMinFrameInterval
		AppendTextBuffer("*!*WARNING:  dwFrameIntervalStep = 0, this invalidates the descriptor when there is a difference between dwMinFrameInterval and dwMaxFrameInterval\r\n");
		OOPS();
	}

	return TRUE;
}

//*****************************************************************************
//
// DisplayUnComDiscreteFrameType()
//
//*****************************************************************************

BOOL
DisplayUnComDiscreteFrameType(
	PVIDEO_FRAME_UNCOMPRESSED UDiscreteDesc
)
{
	//@@DisplayUnComDiscreteFrameType -Uncompressed Discrete Frame
	UINT    iNdex = 1;
	UINT    iCurFrame = 0;
	ULONG* ulFrameInterval = NULL;

	AppendTextBuffer("===>Additional Discrete Frame Type Data\r\n");

	// There are (UDiscreteDesc->bFrameIntervalType) dwFrameIntervals (1 based index)
	for (; iNdex <= UDiscreteDesc->bFrameIntervalType; iNdex++, iCurFrame++)
	{
		ulFrameInterval = &UDiscreteDesc->adwFrameInterval[iCurFrame];
		// To convert the default frame interval, which is in 100 ns units,  to  milliseconds, we divide by 10,000.
		// 100 ns = 10^(-7) seconds = 10^(-7) sec * 1000 msec/sec = 10^(-7) * 10^3 milleseconds = 10^(-4) seconds
		// = 1/10,000 milliseconds


		// To convert the frame interval to Hz, we divide by 10,000,000 and then take the inverse
		AppendTextBuffer("dwFrameInterval[%d]:          0x%08X = %lf mSec (%4.2f Hz)\r\n",
			iNdex, *ulFrameInterval,
			((double)*ulFrameInterval) / 10000.0,
			(10000000.0 / ((double)*ulFrameInterval))
		);
		if (0 == *ulFrameInterval)
		{
			//@@TestCase B18.1 (descript.c line 1061)
			//@@ERROR
			//@@Descriptor Field - dwFrameInterval[x]
			//@@dwFrameInterval[x] must be non-zero
			AppendTextBuffer("*!*ERROR:  dwFrameInterval[%d] must be non-zero\r\n", iNdex);
			OOPS();
		}
		if ((iNdex > 1) && (*ulFrameInterval <= UDiscreteDesc->adwFrameInterval[iCurFrame - 1]))
		{
			//@@TestCase B18.2 (descript.c line 1067)
			//@@ERROR
			//@@Descriptor Field - dwFrameInterval[x]
			//@@dwFrameInterval[n] must be greater than dwFrameInterval[n - 1]
			AppendTextBuffer("*!*ERROR:  dwFrameInterval[0x%02X] must be "\
				"greater than preceding dwFrameInterval[0x%02X]\r\n", iNdex, iNdex - 1);
			OOPS();
		}
	}
	return TRUE;
}

//*****************************************************************************
//
// DisplayMJPEGFormat()
//
//*****************************************************************************

BOOL
DisplayMJPEGFormat(
	PVIDEO_FORMAT_MJPEG MJPEGFormatDesc
)
{
	//@@DisplayMJPEGFormat - MJPEG Format
	// Initialize the default Frame
	g_chMJPEGFrameDefault = MJPEGFormatDesc->bDefaultFrameIndex;

	AppendTextBuffer("\r\n          ===>Video Streaming MJPEG Format Type Descriptor<===\r\n");
	AppendTextBuffer("bLength:                           0x%02X\r\n", MJPEGFormatDesc->bLength);
	AppendTextBuffer("bDescriptorType:                   0x%02X\r\n", MJPEGFormatDesc->bDescriptorType);
	AppendTextBuffer("bDescriptorSubtype:                0x%02X\r\n", MJPEGFormatDesc->bDescriptorSubtype);
	AppendTextBuffer("bFormatIndex:                      0x%02X\r\n", MJPEGFormatDesc->bFormatIndex);
	AppendTextBuffer("bNumFrameDescriptors:              0x%02X\r\n", MJPEGFormatDesc->bNumFrameDescriptors);

	if (MJPEGFormatDesc->bLength != sizeof(VIDEO_FORMAT_MJPEG))
	{
		//@@TestCase B19.1 (descript.c line 1098)
		//@@ERROR
		//@@Descriptor Field - bLength
		//@@The declared length in the device descriptor is not equal to the 
		//@@  required length in the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  bLength of %d incorrect, should be %d\r\n",
			MJPEGFormatDesc->bLength,
			sizeof(VIDEO_FORMAT_MJPEG));
		OOPS();
	}

	if (MJPEGFormatDesc->bFormatIndex == 0)
	{
		//@@TestCase B19.2 (descript.c line 1103)
		//@@ERROR
		//@@Descriptor Field - bFormatIndex
		//@@bFormatIndex is set to zero which is not in accordance with 
		//@@  the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  bFormatIndex must be non-zero\r\n");
		OOPS();
	}

	if (MJPEGFormatDesc->bNumFrameDescriptors == 0)
	{
		//@@TestCase B19.3 (descript.c line 1108)
		//@@ERROR
		//@@Descriptor Field - bNumFrameDescriptors
		//@@bNumFrameDescriptors is set to zero which is not in accordance 
		//@@  with the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  bNumFrameDescriptors must be non-zero\r\n");
		OOPS();
	}

	AppendTextBuffer("bmFlags:                           0x%02X",
		(MJPEGFormatDesc->bmFlags & 0x01));

	//@@TestCase B19.4
	//@@Not yet implemented - Priority 1
	//@@Descriptor Field - bmFlags
	//@@We should validate that reserved bits are set to zero.
	if (gDoAnnotation)
	{
		if (MJPEGFormatDesc->bmFlags & 0x01)
		{
			AppendTextBuffer("  -> Sample Size is Fixed");
		}
		else
		{
			AppendTextBuffer("  -> Sample Size is Not Fixed");
		}
	}
	AppendTextBuffer("\r\nbDefaultFrameIndex:                0x%02X\r\n",
		MJPEGFormatDesc->bDefaultFrameIndex);

	if (MJPEGFormatDesc->bDefaultFrameIndex == 0 ||
		MJPEGFormatDesc->bDefaultFrameIndex >
		MJPEGFormatDesc->bNumFrameDescriptors)
	{
		//@@TestCase B19.5  (descript.c line 1113)
		//@@ERROR
		//@@Descriptor Field - bDefaultFrameIndex
		//@@bDefaultFrameIndex is not in the domain of constrained by 
		//@@  bNumFrameDescriptors
		AppendTextBuffer("*!*ERROR:  bDefaultFrameIndex 0x%02X invalid, should "\
			"be between 1 and 0x%02x/r/n",
			MJPEGFormatDesc->bDefaultFrameIndex,
			MJPEGFormatDesc->bNumFrameDescriptors);
		OOPS();
	}

	AppendTextBuffer("bAspectRatioX:                     0x%02X\r\n",
		MJPEGFormatDesc->bAspectRatioX);
	AppendTextBuffer("bAspectRatioY:                     0x%02X",
		MJPEGFormatDesc->bAspectRatioY);

	if (((MJPEGFormatDesc->bmInterlaceFlags & 0x01) &&
		((MJPEGFormatDesc->bAspectRatioY != 0) &&
			(MJPEGFormatDesc->bAspectRatioX != 0))))
	{
		if (gDoAnnotation)
		{
			AppendTextBuffer("  -> Aspect Ratio is set for a %d:%d display",
				(MJPEGFormatDesc->bAspectRatioX), (MJPEGFormatDesc->bAspectRatioY));
		}
	}
	else
	{
		if (MJPEGFormatDesc->bAspectRatioY != 0 || MJPEGFormatDesc->bAspectRatioX != 0)
		{
			//@@TestCase B19.6
			//@@ERROR
			//@@Descriptor Field - bAspectRatioX and bAspectRatioY
			//@@Verify that that bAspectRatioX and bAspectRatioY are  set to zero 
			//@@  if stream is non-interlaced
			AppendTextBuffer("\r\n*!*ERROR:  bAspectRatioX and bAspectRatioY must "\
				"be 0 if stream non-Interlaced");
			OOPS();
		}
	}
	AppendTextBuffer("\r\nbmInterlaceFlags:                  0x%02X\r\n",
		MJPEGFormatDesc->bmInterlaceFlags);

	if (gDoAnnotation)
	{
		AppendTextBuffer("     D00   = %x %sInterlaced stream or variable\r\n",
			(MJPEGFormatDesc->bmInterlaceFlags & 1),
			(MJPEGFormatDesc->bmInterlaceFlags & 1) ? "" : " non-");
		AppendTextBuffer("     D01   = %x %s per frame\r\n",
			((MJPEGFormatDesc->bmInterlaceFlags >> 1) & 1),
			((MJPEGFormatDesc->bmInterlaceFlags >> 1) & 1) ? " 1 field" : " 2 fields");
		AppendTextBuffer("     D02   = %x  Field 1 %sfirst\r\n",
			((MJPEGFormatDesc->bmInterlaceFlags >> 2) & 1),
			((MJPEGFormatDesc->bmInterlaceFlags >> 2) & 1) ? "" : "not ");
		//@@TestCase B19.7
		//@@Not yet implemented - Priority 1
		//@@Descriptor Field - bmInterlaceFlags
		//@@Validate that reserved bits (D3) are set to zero.
		AppendTextBuffer("     D03   = %x  Reserved%s\r\n",
			((MJPEGFormatDesc->bmInterlaceFlags >> 3) & 1),
			((MJPEGFormatDesc->bmInterlaceFlags >> 3) & 1) ?
			"\r\n*!*ERROR: non zero" : "");
		AppendTextBuffer("     D4..5 = %x  Field patterns  ->",
			((MJPEGFormatDesc->bmInterlaceFlags >> 4) & 3));
		switch (MJPEGFormatDesc->bmInterlaceFlags & 0x30)
		{
		case 0x00:
			AppendTextBuffer(" Field 1 only");
			break;
		case 0x10:
			AppendTextBuffer(" Field 2 only");
			break;
		case 0x20:
			AppendTextBuffer(" Regular Pattern of fields 1 and 2");
			break;
		case 0x30:
			AppendTextBuffer(" Random Pattern of fields 1 and 2");
			break;
		}
		AppendTextBuffer("\r\n     D6..7 = %x  Display Mode  ->",
			((MJPEGFormatDesc->bmInterlaceFlags >> 6) & 3));
		switch (MJPEGFormatDesc->bmInterlaceFlags & 0xC0)
		{
		case 0x00:
			AppendTextBuffer(" Bob only");
			break;
		case 0x40:
			AppendTextBuffer(" Weave only");
			break;
		case 0x80:
			AppendTextBuffer(" Bob or weave");
			break;
		case 0xC0:
			//@@TestCase B19.8
			//@@Not yet implemented - Priority 3
			//@@Descriptor Field - bmInterlaceFlags
			//@@Question - Should we validate that reserved bits are set to zero?
			AppendTextBuffer(" Reserved");
			break;
		}
	}

	//@@TestCase B19.9
	//@@Not yet implemented - Priority 1
	//@@Descriptor Field - bCopyProtect
	//@@Question - Are their reserved bits and should we validate that 
	//@@  reserved bits are set to zero?
	AppendTextBuffer("\r\nbCopyProtect:                      0x%02X",
		MJPEGFormatDesc->bCopyProtect);
	if (gDoAnnotation)
	{
		if (MJPEGFormatDesc->bCopyProtect)
			AppendTextBuffer("  -> Duplication Restricted");
		else
			AppendTextBuffer("  -> Duplication Unrestricted");
	}
	AppendTextBuffer("\r\n");

	// Check that the correct number of Frame Descriptors and one Color Matching
	//  descriptor follow
	CheckForColorMatchingDesc((PVIDEO_SPECIFIC)MJPEGFormatDesc,
		MJPEGFormatDesc->bNumFrameDescriptors, VS_FRAME_MJPEG);

	return TRUE;
}

//*****************************************************************************
//
// DisplayMJPEGFrameType()
//
//*****************************************************************************

BOOL
DisplayMJPEGFrameType(
	PVIDEO_FRAME_MJPEG MJPEGFrameDesc
)
{
	//@@DisplayMJPEGFrameType -MJPEG Frame
	size_t bLength = 0;
	bLength = SizeOfVideoFrameMjpeg(MJPEGFrameDesc);

	AppendTextBuffer("\r\n          ===>Video Streaming MJPEG Frame Type Descriptor<===\r\n");
	if (gDoAnnotation)
	{
		if (MJPEGFrameDesc->bFrameIndex == g_chMJPEGFrameDefault)
		{
			AppendTextBuffer("          --->This is the Default (optimum) Frame index\r\n");
		}
	}
	AppendTextBuffer("bLength:                           0x%02X\r\n", MJPEGFrameDesc->bLength);
	AppendTextBuffer("bDescriptorType:                   0x%02X\r\n", MJPEGFrameDesc->bDescriptorType);
	AppendTextBuffer("bDescriptorSubtype:                0x%02X\r\n", MJPEGFrameDesc->bDescriptorSubtype);
	AppendTextBuffer("bFrameIndex:                       0x%02X\r\n", MJPEGFrameDesc->bFrameIndex);
	AppendTextBuffer("bmCapabilities:                    0x%02X\r\n", MJPEGFrameDesc->bmCapabilities);
	AppendTextBuffer("wWidth:                          0x%04X = %d\r\n", MJPEGFrameDesc->wWidth, MJPEGFrameDesc->wWidth);
	AppendTextBuffer("wHeight:                         0x%04X = %d\r\n", MJPEGFrameDesc->wHeight, MJPEGFrameDesc->wHeight);
	AppendTextBuffer("dwMinBitRate:                0x%08X\r\n", MJPEGFrameDesc->dwMinBitRate);
	AppendTextBuffer("dwMaxBitRate:                0x%08X\r\n", MJPEGFrameDesc->dwMaxBitRate);
	AppendTextBuffer("dwMaxVideoFrameBufferSize:   0x%08X\r\n", MJPEGFrameDesc->dwMaxVideoFrameBufferSize);

	// To convert the default frame interval, which is in 100 ns units,  to  milliseconds, we divide by 10,000.
	// 100 ns = 10^(-7) seconds = 10^(-7) sec * 1000 msec/sec = 10^(-7) * 10^3 milleseconds = 10^(-4) seconds
	// = 1/10,000 milliseconds


	// To convert the frame interval to Hz, we divide by 10,000,000 and then take the inverse

	AppendTextBuffer("dwDefaultFrameInterval:      0x%08X = %lf mSec (%4.2f Hz)\r\n",
		MJPEGFrameDesc->dwDefaultFrameInterval,
		((double)MJPEGFrameDesc->dwDefaultFrameInterval) / 10000.0,
		(10000000.0 / ((double)MJPEGFrameDesc->dwDefaultFrameInterval))
	);
	AppendTextBuffer("bFrameIntervalType:                0x%02X\r\n", MJPEGFrameDesc->bFrameIntervalType);

	if (MJPEGFrameDesc->bLength != bLength)
	{
		//@@TestCase B20.1 (descript.c line 1154)
		//@@ERROR
		//@@Descriptor Field - bLength
		//@@The declared length in the device descriptor is less than required length in the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  bLength of %d is incorrect, should be %d\r\n",
			MJPEGFrameDesc->bLength, bLength);
		OOPS();
	}

	if (MJPEGFrameDesc->bFrameIndex == 0)
	{
		//@@TestCase B20.2  (descript.c line 1159)
		//@@WARNING
		//@@Descriptor Field - bFrameIndex
		//@@bFrameIndex is set to zero which is not in accordance with the USB Video Device Specification
		AppendTextBuffer("*!*WARNING:  bFrameIndex = 0, this invalidates the descriptor\r\n");
		OOPS();
	}

	//@@TestCase B20.3
	//@@Not yet implemented - Priority 1
	//@@Descriptor Field - bmCapabilities
	//@@Question:  Should we try to verify that bmCapabilities is valid?
	//    AppendTextBuffer("bmCapabilities:                    0x%02X\r\n", MJPEGFrameDesc->bmCapabilities);

	if (MJPEGFrameDesc->wWidth == 0)
	{
		//@@TestCase B20.4 (descript.c line 1164)
		//@@ERROR
		//@@Descriptor Field - wWidth
		//@@wWidth is set to zero which is not in accordance with the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  wWidth = 0, this invalidates the descriptor\r\n");
		OOPS();
	}

	if (MJPEGFrameDesc->wHeight == 0)
	{
		//@@TestCase B20.5 (descript.c line 1169)
		//@@ERROR
		//@@Descriptor Field - wHeight
		//@@wHeight is set to zero which is not in accordance with the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  wHeight = 0, this invalidates the descriptor\r\n");
		OOPS();
	}

	if (MJPEGFrameDesc->dwMinBitRate == 0)
	{
		//@@TestCase B20.6 (descript.c line 1174)
		//@@ERROR
		//@@Descriptor Field - dwMinBitRate
		//@@dwMinBitRate is set to zero which is not in accordance with the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  dwMinBitRate = 0, this invalidates the descriptor\r\n");
		OOPS();
	}

	if (MJPEGFrameDesc->dwMaxBitRate == 0)
	{
		//@@TestCase B20.7 (descript.c line 1179)
		//@@ERROR
		//@@Descriptor Field - dwMaxBitRate
		//@@dwMaxBitRate is set to zero which is not in accordance with the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  dwMaxBitRate = 0, this invalidates the descriptor\r\n");
		OOPS();
	}

	if (MJPEGFrameDesc->dwMinBitRate > MJPEGFrameDesc->dwMaxBitRate)
	{
		//@@TestCase B20.8
		//@@ERROR
		//@@Descriptor Field - dwMinBitRate and dwMaxBitRate
		//@@Verify that dwMaxBitRate is greater than dwMinBitRate
		AppendTextBuffer("*!*ERROR:  dwMinBitRate > dwMaxBitRate, this invalidates the descriptor\r\n");
		OOPS();
	}
	else if (MJPEGFrameDesc->bFrameIntervalType == 1 && MJPEGFrameDesc->dwMinBitRate != MJPEGFrameDesc->dwMaxBitRate)
	{
		//@@TestCase B20.9
		//@@WARNING
		//@@Descriptor Field - bFrameIntervalType, dwMinBitRate, and dwMaxBitRate
		//@@Verify that dwMaxBitRate is equal to dwMinBitRate if bFrameIntervalType is 1
		AppendTextBuffer("*!*WARNING:  if bFrameIntervalType is 1 then dwMinBitRate should equal dwMaxBitRate\r\n");
		OOPS();
	}

	if (MJPEGFrameDesc->dwMaxVideoFrameBufferSize == 0)
	{
		//@@TestCase B20.10  (descript.c line 1183)
		//@@ERROR
		//@@Descriptor Field - dwMaxVideoFrameBufferSize
		//@@dwMaxVideoFrameBufferSize is set to zero which is not in accordance with the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  dwMaxVideoFrameBufferSize = 0, this invalidates the descriptor\r\n");
		OOPS();
	}

	if (MJPEGFrameDesc->dwMaxVideoFrameBufferSize == 0)
	{
		//@@TestCase B20.11  (descript.c line 1188)
		//@@ERROR
		//@@Descriptor Field - dwDefaultFrameInterval
		//@@dwDefaultFrameInterval is set to zero which is not in accordance with the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  dwDefaultFrameInterval = 0, this invalidates the descriptor\r\n");
		OOPS();
	}

	if (0 == MJPEGFrameDesc->bFrameIntervalType)
	{
		DisplayMJPEGContinuousFrameType(MJPEGFrameDesc);
	}
	else
	{
		DisplayMJPEGDiscreteFrameType(MJPEGFrameDesc);
	}

	return TRUE;
}


//*****************************************************************************
//
// DisplayMJPEGContinuousFrameType()
//
//*****************************************************************************

BOOL
DisplayMJPEGContinuousFrameType(
	PVIDEO_FRAME_MJPEG MContinuousDesc
)
{
	//@@DisplayMJPEGContinuousFrameType - MJPEG Continuous Frame
	ULONG dwMinFrameInterval = MContinuousDesc->adwFrameInterval[0];
	ULONG dwMaxFrameInterval = MContinuousDesc->adwFrameInterval[1];
	ULONG dwFrameIntervalStep = MContinuousDesc->adwFrameInterval[2];

	AppendTextBuffer("===>Additional Continuous Frame Type Data\r\n");
	// To convert the default frame interval, which is in 100 ns units,  to  milliseconds, we divide by 10,000.
	// 100 ns = 10^(-7) seconds = 10^(-7) sec * 1000 msec/sec = 10^(-7) * 10^3 milleseconds = 10^(-4) seconds
	// = 1/10,000 milliseconds


	// To convert the frame interval to Hz, we divide by 10,000,000 and then take the inverse


	AppendTextBuffer("dwMinFrameInterval:          0x%08X = %lf mSec (%d Hz)\r\n",
		dwMinFrameInterval,
		((double)dwMinFrameInterval) / 10000.0,
		(ULONG)(10000000.0 / ((double)dwMinFrameInterval) + 0.5));

	AppendTextBuffer("dwMaxFrameInterval:          0x%08X = %lf mSec (%d Hz)\r\n",
		dwMaxFrameInterval,
		((double)dwMaxFrameInterval) / 10000.0,
		(ULONG)(10000000.0 / ((double)dwMaxFrameInterval) + 0.5));

	AppendTextBuffer("dwFrameIntervalStep:         0x%08X\r\n", dwFrameIntervalStep);

	if (dwMinFrameInterval == 0)
	{
		//@@TestCase B21.2   (descript.c line 1188)
		//@@ERROR
		//@@Descriptor Field - dwMinFrameInterval
		//@@dwMinFrameInterval is set to zero which is not in accordance with the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  dwMinFrameInterval = 0, this invalidates the descriptor\r\n");
		OOPS();
	}

	if (dwMaxFrameInterval == 0)
	{
		//@@TestCase B21.3  (descript.c line 1188)
		//@@ERROR
		//@@Descriptor Field - dwMaxFrameInterval
		//@@dwMaxFrameInterval is set to zero which is not in accordance with the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  dwMaxFrameInterval = 0, this invalidates the descriptor\r\n");
		OOPS();
	}

	if (dwMinFrameInterval > dwMaxFrameInterval)
	{
		//@@TestCase B21.4  (descript.c line 1211)
		//@@ERROR
		//@@Descriptor Field - dwMinFrameInterval and dwMaxFrameInterval
		//@@Verify that dwMaxFrameInterval is greater than dwMinFrameInterval
		AppendTextBuffer("*!*ERROR:  dwMinFrameInterval is larger that dwMaxFrameInterval, this invalidates the descriptor\r\n");
		OOPS();
	}
	else if ((dwMinFrameInterval + dwFrameIntervalStep) > dwMaxFrameInterval)
	{
		//@@TestCase B21.5
		//@@WARNING
		//@@Descriptor Field - dwFrameIntervalStep, dwMinFrameInterval, and dwMaxFrameInterval
		//@@Verify that dwMaxFrameInterval is greater than dwMinFrameInterval combined with dwFrameIntervalStep
		AppendTextBuffer("*!*WARNING:  dwMinFrameInterval + dwFrameIntervalStep is greater than dwMaxFrameInterval, this could cause problems\r\n");
		OOPS();
	}
	else if ((dwMaxFrameInterval - dwMinFrameInterval) == 0)
	{
		//@@TestCase B21.6
		//@@CAUTION
		//@@Descriptor Field - dwFrameIntervalStep
		//@@Suggestion to use descrite frames if dwFrameIntervalStep is zero
		AppendTextBuffer("*!*CAUTION:  dwFrameIntervalStep equals zero, consider using discrete frames\r\n");
		OOPS();
	}
	else if ((dwMaxFrameInterval - dwMinFrameInterval) % dwFrameIntervalStep)
	{
		//@@TestCase B21.7  (descript.c line 1220)
		//@@WARNING
		//@@Descriptor Field - dwFrameIntervalStep, dwMinFrameInterval, and dwMaxFrameInterval
		//@@Verify that the difference between dwMaxFrameInterval and dwMinFrameInterval is evenly divisible by dwFrameIntervalStep
		AppendTextBuffer("*!*WARNING:  dwMaxFrameInterval minus dwMinFrameInterval is not evenly divisible by dwFrameIntervalStep, this could cause problems\r\n");
		OOPS();
	}

	if (dwFrameIntervalStep == 0 && (dwMaxFrameInterval - dwMinFrameInterval))
	{
		//@@TestCase B21.8 (descript.c line 1200)
		//@@WARNING
		//@@Descriptor Field - dwFrameIntervalStep, dwMinFrameInterval, and dwMaxFrameInterval
		//@@Verify that the dwFrameIntervalStep is not zero if there is a difference between dwMaxFrameInterval and dwMinFrameInterval
		AppendTextBuffer("*!*WARNING:  dwFrameIntervalStep = 0, this invalidates the descriptor when there is a difference between \r\n          *!*dwMinFrameInterval and dwMaxFrameInterval\r\n");
		OOPS();
	}

	return TRUE;
}


//*****************************************************************************
//
// DisplayMJPEGDiscreteFrameType()
//
//*****************************************************************************

BOOL
DisplayMJPEGDiscreteFrameType(
	PVIDEO_FRAME_MJPEG MDiscreteDesc
)
{
	//@@DisplayMJPEGDiscreteFrameType -MJPEG Discrete Frame
	UINT    iNdex = 1;
	UINT    iCurFrame = 0;
	ULONG* ulFrameInterval = NULL;

	AppendTextBuffer("===>Additional Discrete Frame TypeData\r\n");

	// There are (MDiscreteDesc->bFrameIntervalType) dwFrameIntervals (1 based index)
	for (; iNdex <= MDiscreteDesc->bFrameIntervalType; iNdex++, iCurFrame++)
	{
		ulFrameInterval = &MDiscreteDesc->adwFrameInterval[iCurFrame];
		// To convert the default frame interval, which is in 100 ns units,  to  milliseconds, we divide by 10,000.
		// 100 ns = 10^(-7) seconds = 10^(-7) sec * 1000 msec/sec = 10^(-7) * 10^3 milleseconds = 10^(-4) seconds
		// = 1/10,000 milliseconds


		// To convert the frame interval to Hz, we divide by 10,000,000 and then take the inverse
		AppendTextBuffer("dwFrameInterval[%d]:          0x%08X = %lf mSec (%4.2f Hz)\r\n",
			iNdex, *ulFrameInterval,
			((double)*ulFrameInterval) / 10000.0,
			(10000000.0 / ((double)*ulFrameInterval))
		);
		if (0 == *ulFrameInterval)
		{
			//@@TestCase B22.1 (descript.c line 1229)
			//@@ERROR
			//@@Descriptor Field - dwFrameInterval[x]
			//@@dwFrameInterval[x] must be non-zero
			AppendTextBuffer("*!*ERROR:  dwFrameInterval[%d] must be non-zero\r\n", iNdex);
			OOPS();
		}
		if ((iNdex > 1) && (*ulFrameInterval <= MDiscreteDesc->adwFrameInterval[iCurFrame - 1]))
		{
			//@@TestCase B22.2 (descript.c line 1235)
			//@@ERROR
			//@@Descriptor Field - dwFrameInterval[x]
			//@@dwFrameInterval[n] must be greater than dwFrameInterval[n - 1]
			AppendTextBuffer("*!*ERROR:  dwFrameInterval[0x%02X] must be "\
				"greater than preceding dwFrameInterval[0x%02X]\r\n", iNdex, iNdex - 1);
			OOPS();
		}
	}
	return TRUE;
}

//*****************************************************************************
//
// DisplayMPEG1SSFormat()
//
//*****************************************************************************

BOOL
DisplayMPEG1SSFormat(
	PVIDEO_FORMAT_MPEG1SS MPEG1SSFormatDesc
)
{
	//@@DisplayMPEG1SSFormat -MPEG1 SS Format
	AppendTextBuffer("\r\n          ===>Video Streaming MPEG1-SS Format Type Descriptor<===\r\n");
	AppendTextBuffer("bLength:                           0x%02X\r\n", MPEG1SSFormatDesc->bLength);
	AppendTextBuffer("bDescriptorType:                   0x%02X\r\n", MPEG1SSFormatDesc->bDescriptorType);
	AppendTextBuffer("bDescriptorSubtype:                0x%02X\r\n", MPEG1SSFormatDesc->bDescriptorSubtype);
	AppendTextBuffer("bFormatIndex:                      0x%02X\r\n", MPEG1SSFormatDesc->bFormatIndex);
	AppendTextBuffer("wPacketLength:                     0x%02X\r\n", MPEG1SSFormatDesc->bPacketLength);
	AppendTextBuffer("wPackLength:                       0x%02X\r\n", MPEG1SSFormatDesc->bPackLength);
	AppendTextBuffer("bPackdataType:                     0x%02X", (MPEG1SSFormatDesc->bPackDataType));
	if (gDoAnnotation) {
		if (MPEG1SSFormatDesc->bPackDataType & 0x01) { AppendTextBuffer("  -> Pack data size fixed\r\n"); }
		else { AppendTextBuffer("  -> Pack data size variable\r\n"); }
	}
	else { AppendTextBuffer("\r\n"); }


	if (MPEG1SSFormatDesc->bLength != sizeof(VIDEO_FORMAT_MPEG1SS))
	{
		//@@TestCase B23.1 (descript.c line 1514)
		//@@ERROR
		//@@Descriptor Field - bLength
		//@@The declared length in the device descriptor is not equal to the required length in the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  bLength of %d incorrect, should be %d.  USBView cannot correctly display descriptor\r\n",
			MPEG1SSFormatDesc->bLength,
			sizeof(VIDEO_FORMAT_MPEG1SS));
		OOPS();
	}

	if (MPEG1SSFormatDesc->bFormatIndex == 0)
	{
		//@@TestCase B23.2 (descript.c line 1519)
		//@@WARNING
		//@@Descriptor Field - bFormatIndex
		//@@bFormatIndex is set to zero which is not in accordance with the USB Video Device Specification
		AppendTextBuffer("*!*WARNING:  bFormatIndex = 0, this invalidates the descriptor\r\n");
		OOPS();
	}

	//@@TestCase B23.3
	//@@Not yet implemented - Priority 1
	//@@Descriptor Field - bPackdataType
	//@@Question - Should we validate that reserved bits are set to zero?
	//    AppendTextBuffer("bPackdataType:                     0x%02X", (MPEG1SSFormatDesc->bPackdataType & 0x01));

	// This descriptor is deprecated for UVC 1.1
#ifdef H264_SUPPORT
	if (UVC10 != g_chUVCversion)
	{
		AppendTextBuffer("*!*ERROR: This format is NOT ALLOWED for UVC version >= 1.1 devices\r\n");
	}
#else
	if (UVC11 == g_chUVCversion)
	{
		AppendTextBuffer("*!*ERROR: This format is NOT ALLOWED for UVC 1.1 devices\r\n");
	}
#endif

	return TRUE;
}


//*****************************************************************************
//
// DisplayMPEG2PSFormat()
//
//*****************************************************************************

BOOL
DisplayMPEG2PSFormat(
	PVIDEO_FORMAT_MPEG2PS MPEG2PSFormatDesc
)
{
	//@@DisplayMPEG2PSFormat -MPEG2 PS Format
	AppendTextBuffer("\r\n          ===>Video Streaming MPEG2-PS Format Type Descriptor<===\r\n");
	AppendTextBuffer("bLength:                           0x%02X\r\n", MPEG2PSFormatDesc->bLength);
	AppendTextBuffer("bDescriptorType:                   0x%02X\r\n", MPEG2PSFormatDesc->bDescriptorType);
	AppendTextBuffer("bDescriptorSubtype:                0x%02X\r\n", MPEG2PSFormatDesc->bDescriptorSubtype);
	AppendTextBuffer("bFormatIndex:                      0x%02X\r\n", MPEG2PSFormatDesc->bFormatIndex);
	AppendTextBuffer("bPacketLength:                     0x%02X\r\n", MPEG2PSFormatDesc->bPacketLength);
	AppendTextBuffer("bPackLength:                       0x%02X\r\n", MPEG2PSFormatDesc->bPackLength);
	AppendTextBuffer("bPackDataType:                     0x%02X", (MPEG2PSFormatDesc->bPackDataType));

	if (MPEG2PSFormatDesc->bLength != sizeof(VIDEO_FORMAT_MPEG2PS))
	{
		//@@TestCase B24.1 (descript.c line 1542)
		//@@ERROR
		//@@Descriptor Field - bLength
		//@@The declared length in the device descriptor is not equal to the required length in the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  bLength of %d incorrect, should be %d.  USBView cannot correctly display descriptor\r\n",
			MPEG2PSFormatDesc->bLength,
			sizeof(VIDEO_FORMAT_MPEG2PS));
		OOPS();
		AppendTextBuffer("*!*USBView will try to display the rest of the descriptor but results may not be accurate\r\n");
	}

	if (MPEG2PSFormatDesc->bFormatIndex == 0)
	{
		//@@TestCase B24.2 (descript.c line 1547)
		//@@WARNING
		//@@Descriptor Field - bFormatIndex
		//@@bFormatIndex is set to zero which is not in accordance with the USB Video Device Specification
		AppendTextBuffer("*!*WARNING:  bFormatIndex = 0, this invalidates the descriptor\r\n");
		OOPS();
	}

	//@@TestCase B24.3
	//@@Not yet implemented - Priority 1
	//@@Descriptor Field - bPackdataType
	//@@Question - Should we validate that reserved bits are set to zero?
	//    AppendTextBuffer("bPackdataType:                     0x%02X", (MPEG2PSFormatDesc->bPackdataType & 0x01));

	// This descriptor is deprecated for UVC 1.1
#ifdef H264_SUPPORT
	if (UVC10 != g_chUVCversion)
	{
		AppendTextBuffer("*!*ERROR: This format is NOT ALLOWED for UVC version >= 1.1 devices\r\n");
	}
#else
	if (UVC11 == g_chUVCversion)
	{
		AppendTextBuffer("*!*ERROR: This format is NOT ALLOWED for UVC 1.1 devices\r\n");
	}
#endif

	return TRUE;

}


//*****************************************************************************
//
// DisplayMPEG2TSFormat()
//
//*****************************************************************************

BOOL
DisplayMPEG2TSFormat(
	PVIDEO_FORMAT_MPEG2TS MPEG2TSFormatDesc
)
{
	//@@DisplayMPEG2TSFormat -MPEG2 TS Format
	UCHAR bLength = sizeof(VIDEO_FORMAT_MPEG2TS);

	AppendTextBuffer("\r\n          ===>Video Streaming MPEG2-TS Format Type Descriptor<===\r\n");
	AppendTextBuffer("bLength:                           0x%02X\r\n", MPEG2TSFormatDesc->bLength);
	AppendTextBuffer("bDescriptorType:                   0x%02X\r\n", MPEG2TSFormatDesc->bDescriptorType);
	AppendTextBuffer("bDescriptorSubtype:                0x%02X\r\n", MPEG2TSFormatDesc->bDescriptorSubtype);
	AppendTextBuffer("bFormatIndex:                      0x%02X\r\n", MPEG2TSFormatDesc->bFormatIndex);
	AppendTextBuffer("bDataOffset:                       0x%02X\r\n", MPEG2TSFormatDesc->bDataOffset);
	AppendTextBuffer("bPacketLength:                     0x%02X\r\n", MPEG2TSFormatDesc->bPacketLength);
	AppendTextBuffer("bStrideLength:                     0x%02X\r\n", MPEG2TSFormatDesc->bStrideLength);

#ifdef H264_SUPPORT
	if (UVC10 != g_chUVCversion)
#else
	if (UVC11 == g_chUVCversion)
#endif
	{
		int     i = 0;
		PCHAR   pStr = NULL;
		OLECHAR szGUID[256];
		GUID* pStrideGuid = NULL;

		pStrideGuid = (GUID*)(&MPEG2TSFormatDesc->bStrideLength + 1);

		memset((LPOLESTR)szGUID, 0, sizeof(OLECHAR) * 256);
		i = StringFromGUID2((REFGUID)pStrideGuid, (LPOLESTR)szGUID, 255);
		i++;
		AppendTextBuffer("guidStrideFormat:                  %S", szGUID);
		pStr = VidFormatGUIDCodeToName((REFGUID)pStrideGuid);
		if (gDoAnnotation)
		{
			if (pStr)
			{
				AppendTextBuffer(" = %s Format", pStr);
			}
		}
		AppendTextBuffer("\r\n");
		bLength = sizeof(VIDEO_FORMAT_MPEG2TS) + sizeof(GUID);
	}

	if (MPEG2TSFormatDesc->bLength != bLength)
	{
		//@@TestCase B25.1 (descript.c line 1486)
		//@@ERROR
		//@@Descriptor Field - bLength
		//@@The declared length in the device descriptor is not equal to the required length in the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  bLength of %d incorrect, should be %d\r\n",
			MPEG2TSFormatDesc->bLength,
			sizeof(VIDEO_FORMAT_MPEG2TS));
		OOPS();
	}

	if (MPEG2TSFormatDesc->bFormatIndex == 0)
	{
		//@@TestCase B25.2 (descript.c line 1491)
		//@@WARNING
		//@@Descriptor Field - bFormatIndex
		//@@bFormatIndex is set to zero which is not in accordance with the USB Video Device Specification
		AppendTextBuffer("*!*WARNING:  bFormatIndex = 0, this invalidates the descriptor\r\n");
		OOPS();
	}

	//@@TestCase B25.3
	//@@Not yet implemented - Priority 1
	//@@Descriptor Field - bDataOffset, wPacket and wStride
	//@@Question - Should we check that if bDataOffset is 0 that wPacket and wStride should equal each other
	//    AppendTextBuffer("bDataOffset:                       0x%02X\r\n", MPEG2TSFormatDesc->bDataOffset);

	return TRUE;
}


//*****************************************************************************
//
// DisplayMPEG4SLFormat()
//
//*****************************************************************************

BOOL
DisplayMPEG4SLFormat(
	PVIDEO_FORMAT_MPEG4SL MPEG4SLFormatDesc
)
{
	//@@DisplayMPEG4SLFormat -MPEG4 SL Format

	AppendTextBuffer("\r\n          ===>Video Streaming MPEG4-SL Format Type Descriptor<===\r\n");
	AppendTextBuffer("bLength:                           0x%02X\r\n", MPEG4SLFormatDesc->bLength);
	AppendTextBuffer("bDescriptorType:                   0x%02X\r\n", MPEG4SLFormatDesc->bDescriptorType);
	AppendTextBuffer("bDescriptorSubtype:                0x%02X\r\n", MPEG4SLFormatDesc->bDescriptorSubtype);
	AppendTextBuffer("bFormatIndex:                      0x%02X\r\n", MPEG4SLFormatDesc->bFormatIndex);
	AppendTextBuffer("bPacketLength:                     0x%02X\r\n", MPEG4SLFormatDesc->bPacketLength);

	if (MPEG4SLFormatDesc->bLength != sizeof(VIDEO_FORMAT_MPEG4SL))
	{
		//@@TestCase B26.1 (descript.c line 1568)
		//@@ERROR
		//@@Descriptor Field - bLength
		//@@The declared length in the device descriptor is not equal to the required length in the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  bLength of %d incorrect, should be %d.  USBView cannot correctly display descriptor\r\n",
			MPEG4SLFormatDesc->bLength,
			sizeof(VIDEO_FORMAT_MPEG4SL));
		OOPS();
	}

	if (MPEG4SLFormatDesc->bFormatIndex == 0)
	{
		//@@TestCase B26.2 (descript.c line 1573)
		//@@WARNING
		//@@Descriptor Field - bFormatIndex
		//@@bFormatIndex is set to zero which is not in accordance with the USB Video Device Specification
		AppendTextBuffer("*!*WARNING:  bFormatIndex = 0, this invalidates the descriptor\r\n");
		OOPS();
	}

	// This descriptor is deprecated for UVC 1.1
#ifdef H264_SUPPORT
	if (UVC10 != g_chUVCversion)
	{
		AppendTextBuffer("*!*ERROR: This format is NOT ALLOWED for UVC version >= 1.1 devices\r\n");
	}
#else
	if (UVC11 == g_chUVCversion)
	{
		AppendTextBuffer("*!*ERROR: This format is NOT ALLOWED for UVC 1.1 devices\r\n");
	}
#endif
	return TRUE;
}


//*****************************************************************************
//
// DisplayStreamPayload()
//
//*****************************************************************************

BOOL
DisplayStreamPayload(
	PVIDEO_FORMAT_STREAM StreamPayloadDesc
)
{
	//@@DisplayStreamPayload -Stream Based Payload Format
	PCHAR pStr = NULL;
	OLECHAR szGUID[256];
	int i = 0;

	memset((LPOLESTR)szGUID, 0, sizeof(OLECHAR) * 256);
	i = StringFromGUID2((REFGUID)&StreamPayloadDesc->guidFormat, (LPOLESTR)szGUID, 255);
	i++;

	AppendTextBuffer("\r\n          ===>Video Streaming Stream Based Payload Format Type Descriptor<===\r\n");
	AppendTextBuffer("bLength:                           0x%02X\r\n", StreamPayloadDesc->bLength);
	AppendTextBuffer("bDescriptorType:                   0x%02X\r\n", StreamPayloadDesc->bDescriptorType);
	AppendTextBuffer("bDescriptorSubtype:                0x%02X\r\n", StreamPayloadDesc->bDescriptorSubtype);
	AppendTextBuffer("bFormatIndex:                      0x%02X\r\n", StreamPayloadDesc->bFormatIndex);
	AppendTextBuffer("guidFormat:                        %S", szGUID);

	pStr = VidFormatGUIDCodeToName((REFGUID)&StreamPayloadDesc->guidFormat);
	if (gDoAnnotation)
	{
		if (pStr)
		{
			AppendTextBuffer(" = %s Format", pStr);
		}
	}
	AppendTextBuffer("\r\n");
	AppendTextBuffer("dwPacketLength:                    0x%02X\r\n", StreamPayloadDesc->dwPacketLength);

	if (StreamPayloadDesc->bLength != sizeof(VIDEO_FORMAT_STREAM))
	{
		//@@ERROR
		//@@Descriptor Field - bLength
		//@@The declared length in the device descriptor is not equal to the required length in the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  bLength of %d incorrect, should be %d\r\n",
			StreamPayloadDesc->bLength,
			sizeof(PVIDEO_FORMAT_STREAM));
		OOPS();
	}

	if (StreamPayloadDesc->bFormatIndex == 0)
	{
		//@@WARNING
		//@@Descriptor Field - bFormatIndex
		//@@bFormatIndex is set to zero which is not in accordance with the USB Video Device Specification
		AppendTextBuffer("*!*WARNING:  bFormatIndex = 0, this is a 1 based index\r\n");
		OOPS();
	}

	// This descriptor is new for UVC 1.1
	if (UVC10 == g_chUVCversion)
	{
		AppendTextBuffer("*!*ERROR: This format is NOT ALLOWED for UVC 1.0 devices\r\n");
	}
	return TRUE;
}

//*****************************************************************************
//
// DisplayDVFormat()
//
//*****************************************************************************

BOOL
DisplayDVFormat(
	PVIDEO_FORMAT_DV DVFormatDesc
)
{
	//@@DisplayDVFormat -Digital Video Format

	AppendTextBuffer("\r\n          ===>Video Streaming DV Format Type Descriptor<===\r\n");
	AppendTextBuffer("bLength:                           0x%02X\r\n", DVFormatDesc->bLength);
	AppendTextBuffer("bDescriptorType:                   0x%02X\r\n", DVFormatDesc->bDescriptorType);
	AppendTextBuffer("bDescriptorSubtype:                0x%02X\r\n", DVFormatDesc->bDescriptorSubtype);
	AppendTextBuffer("bFormatIndex:                      0x%02X\r\n", DVFormatDesc->bFormatIndex);
	AppendTextBuffer("dwMaxVideoFrameBufferSize:   0x%08X\r\n", DVFormatDesc->dwMaxVideoFrameBufferSize);
	AppendTextBuffer("bFormatType:                       0x%02X\r\n", DVFormatDesc->bFormatType);
	if (gDoAnnotation)
	{
		AppendTextBuffer("     D0..6 = Format Type  ->");
		switch (DVFormatDesc->bFormatType & 0x03)
		{
		case 0x00:
			AppendTextBuffer(" SD-DV\r\n");
			break;
		case 0x01:
			AppendTextBuffer(" SDL-DV\r\n");
			break;
		case 0x02:
			AppendTextBuffer(" HD-DV\r\n");
			break;
		default:
			AppendTextBuffer(" Unknown Format\r\n");
			break;
		}
		if (DVFormatDesc->bFormatType & 0x80)
			AppendTextBuffer("     D7    = 60Hz");
		else
			AppendTextBuffer("     D7    = 50Hz");
		AppendTextBuffer("\r\n");
	}

	if (DVFormatDesc->bLength != sizeof(VIDEO_FORMAT_DV))
	{
		//@@TestCase B27.1 (descript.c line 1453)
		//@@ERROR
		//@@Descriptor Field - bLength
		//@@The declared length in the device descriptor is not equal to the required length in the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  bLength of %d incorrect, should be %d\r\n",
			DVFormatDesc->bLength,
			sizeof(VIDEO_FORMAT_DV));
		OOPS();
	}

	if (DVFormatDesc->bFormatIndex == 0)
	{
		//@@TestCase B27.2 (descript.c line 1458)
		//@@ERROR
		//@@Descriptor Field - bFormatIndex
		//@@bFormatIndex invalid
		AppendTextBuffer("*!*ERROR:  bFormatIndex of 0x%02X is invalid\r\n",
			DVFormatDesc->bFormatIndex);
		OOPS();
	}

	if (DVFormatDesc->dwMaxVideoFrameBufferSize == 0)
	{
		//@@TestCase B27.3 (descript.c line 1463)
		//@@ERROR
		//@@Descriptor Field - dwMaxVideoFrameBufferSize
		//@@dwMaxVideoFrameBufferSize invalid
		AppendTextBuffer("*!*ERROR:  dwMaxVideoFrameBufferSize of 0x%02X is invalid\r\n",
			DVFormatDesc->dwMaxVideoFrameBufferSize);
		OOPS();
	}

	//@@TestCase B27.4
	//@@Not yet implemented - Priority 1
	//@@Descriptor Field - bFormatType
	//@@Question - Should we validate that reserved bits are set to zero?

	return TRUE;
}


//*****************************************************************************
//
// DisplayVidVendorFormat()
//
//*****************************************************************************

BOOL
DisplayVendorVidFormat(
	PVIDEO_FORMAT_VENDOR VendorVidFormatDesc
)
{
	//@@DisplayVendorVidFormat -Vendor Video Format
	OLECHAR szGUID[256];
	int i = 0;

	// Initialize the default Frame
	g_chVendorFrameDefault = VendorVidFormatDesc->bDefaultFrameIndex;

	memset((LPOLESTR)szGUID, 0, sizeof(OLECHAR) * 256);
	i = StringFromGUID2((REFGUID)&VendorVidFormatDesc->guidMajorFormat, (LPOLESTR)szGUID, 255);
	i++;

	AppendTextBuffer("\r\n          ===>Video Streaming Vendor Video Format Type Descriptor<===\r\n");
	AppendTextBuffer("bLength:                           0x%02X\r\n", VendorVidFormatDesc->bLength);
	AppendTextBuffer("bDescriptorType:                   0x%02X\r\n", VendorVidFormatDesc->bDescriptorType);
	AppendTextBuffer("bDescriptorSubtype:                0x%02X\r\n", VendorVidFormatDesc->bDescriptorSubtype);
	AppendTextBuffer("bFormatIndex:                      0x%02X\r\n", VendorVidFormatDesc->bFormatIndex);
	AppendTextBuffer("bNumFrameDescriptors:              0x%02X\r\n", VendorVidFormatDesc->bNumFrameDescriptors);
	AppendTextBuffer("guidMajorFormat:                   %S\r\n", szGUID);
	i = StringFromGUID2((REFGUID)&VendorVidFormatDesc->guidSubFormat, (LPOLESTR)szGUID, 255);
	i++;
	AppendTextBuffer("guidSubFormat:                     %S\r\n", szGUID);
	i = StringFromGUID2((REFGUID)&VendorVidFormatDesc->guidSpecifier, (LPOLESTR)szGUID, 255);
	i++;
	AppendTextBuffer("guidSpecifier:                     %S\r\n", szGUID);
	AppendTextBuffer("bPayloadClass:                     0x%02X\r\n", VendorVidFormatDesc->bPayloadClass);
	AppendTextBuffer("bDefaultFrameIndex:                0x%02X\r\n", VendorVidFormatDesc->bDefaultFrameIndex);
	AppendTextBuffer("bCopyProtect:                      0x%02X", VendorVidFormatDesc->bCopyProtect);
	if (gDoAnnotation) {
		if (VendorVidFormatDesc->bCopyProtect) { AppendTextBuffer("  -> Duplication Restricted\r\n"); }
		else { AppendTextBuffer("  -> Duplication Unrestricted\r\n"); }
	}
	else { AppendTextBuffer("\r\n"); }

	if (VendorVidFormatDesc->bLength != sizeof(VIDEO_FORMAT_VENDOR))
	{
		//@@TestCase B28.1 (descript.c line 1297)
		//@@ERROR
		//@@Descriptor Field - bLength
		//@@The declared length in the device descriptor is not equal to the required length in the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  bLength of %d incorrect, should be %d.  USBView cannot correctly display descriptor\r\n",
			VendorVidFormatDesc->bLength,
			sizeof(VIDEO_FORMAT_VENDOR));
		OOPS();
	}

	if (VendorVidFormatDesc->bFormatIndex == 0)
	{
		//@@TestCase B28.2 (descript.c line 1302)
		//@@ERROR
		//@@Descriptor Field - bFormatIndex
		//@@bFormatIndex is set to zero which is not in accordance with the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  bFormatIndex = 0, this invalidates the descriptor\r\n");
		OOPS();
	}

	if (VendorVidFormatDesc->bNumFrameDescriptors == 0)
	{
		//@@TestCase B28.3 (descript.c line 1307)
		//@@ERROR
		//@@Descriptor Field - bNumFrameDescriptors
		//@@bNumFrameDescriptors is set to zero which is not in accordance with the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  bNumFrameDescriptors = 0, this invalidates the descriptor\r\n");
		OOPS();
	}

	if (VendorVidFormatDesc->bPayloadClass > 1)
	{
		//@@TestCase B28.4
		//@@WARNING
		//@@Descriptor Field - bPayloadClass
		//@@bPayloadClass is using reserved space
		AppendTextBuffer("*!*WARNING:  bPayloadClass is incorrectly using reserved space\r\n");
		OOPS();
	}
	else
	{
		if (gDoAnnotation)
		{
			if (VendorVidFormatDesc->bPayloadClass == 1) { AppendTextBuffer("  -> Using a Frame Based Payload\r\n"); }
			else { AppendTextBuffer("  -> Using a Stream Based Payload\r\n"); }
		}
		else { AppendTextBuffer("\r\n"); }
	}

	if (VendorVidFormatDesc->bDefaultFrameIndex == 0)
	{
		//@@TestCase B28.5 (descript.c line 1312)
		//@@ERROR
		//@@Descriptor Field - bDefaultFrameIndex
		//@@bDefaultFrameIndex is set to zero which is not in accordance with the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  bDefaultFrameIndex = 0, this invalidates the descriptor\r\n");
		OOPS();
	}

	if (VendorVidFormatDesc->bDefaultFrameIndex == 0 || VendorVidFormatDesc->bDefaultFrameIndex > VendorVidFormatDesc->bNumFrameDescriptors)
	{
		//@@TestCase B28.6
		//@@WARNING
		//@@Descriptor Field - bDefaultFrameIndex
		//@@bDefaultFrameIndex is out of range
		AppendTextBuffer("*!*WARNING:  The value %d for the bDefaultFrameIndex is out of range this invalidates the descriptor\r\n*!* The proper range is 1 to %d)",
			VendorVidFormatDesc->bDefaultFrameIndex,
			VendorVidFormatDesc->bNumFrameDescriptors);
		OOPS();
	}

	//@@TestCase B28.7
	//@@Not yet implemented - Priority 1
	//@@Descriptor Field - bCopyProtect
	//@@Question - Are their reserved bits and should we validate that reserved bits are set to zero?
	//    AppendTextBuffer("bCopyProtect:                      0x%02X", VendorVidFormatDesc->bCopyProtect);

	// Check that the correct number of Frame Descriptors and one Color Matching
	//  descriptor follow
	CheckForColorMatchingDesc((PVIDEO_SPECIFIC)VendorVidFormatDesc,
		VendorVidFormatDesc->bNumFrameDescriptors, VS_FRAME_VENDOR);

	// This descriptor is deprecated for UVC 1.1
#ifdef H264_SUPPORT
	if (UVC10 != g_chUVCversion)
	{
		AppendTextBuffer("*!*ERROR: This format is NOT ALLOWED for UVC version >= 1.1 devices\r\n");
	}
#else
	if (UVC11 == g_chUVCversion)
	{
		AppendTextBuffer("*!*ERROR: This format is NOT ALLOWED for UVC 1.1 devices\r\n");
	}
#endif
	return TRUE;
}


//*****************************************************************************
//
// DisplayVendorVidFrameType()
//
//*****************************************************************************

BOOL
DisplayVendorVidFrameType(
	PVIDEO_FRAME_VENDOR VendorVidFrameDesc
)
{
	//@@DisplayVendorVidFrameType -Vendor Video Frame
	size_t bLength = 0;
	bLength = SizeOfVideoFrameVendor(VendorVidFrameDesc);

	AppendTextBuffer("\r\n          ===>Video Streaming Vendor Video Frame Type Descriptor<===\r\n");
	if (gDoAnnotation)
	{
		if (VendorVidFrameDesc->bFrameIndex == g_chVendorFrameDefault)
		{
			AppendTextBuffer("          --->This is the Default (optimum) Frame index\r\n");
		}
	}
	AppendTextBuffer("bLength:                           0x%02X\r\n", VendorVidFrameDesc->bLength);
	AppendTextBuffer("bDescriptorType:                   0x%02X\r\n", VendorVidFrameDesc->bDescriptorType);
	AppendTextBuffer("bDescriptorSubtype:                0x%02X\r\n", VendorVidFrameDesc->bDescriptorSubtype);
	AppendTextBuffer("bFrameIndex:                       0x%02X\r\n", VendorVidFrameDesc->bFrameIndex);

	if (VendorVidFrameDesc->bLength != bLength)
	{
		//@@TestCase B29.1 (descript.c line 1352)
		//@@ERROR
		//@@Descriptor Field - bLength
		//@@The declared length in the device descriptor is less than required length in the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  bLength of %d incorrect, should be %d\r\n",
			VendorVidFrameDesc->bLength, bLength);
		OOPS();
	}

	if (VendorVidFrameDesc->bFrameIndex == 0)
	{
		//@@TestCase B29.2 (descript.c line 1357)
		//@@ERROR
		//@@Descriptor Field - bFrameIndex
		//@@bFrameIndex is set to zero which is not in accordance with the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  bFrameIndex = 0, this is a 1 based index\r\n");
		OOPS();
	}

	AppendTextBuffer("bmCapabilities:                    0x%02X", VendorVidFrameDesc->bmCapabilities);

	if (VendorVidFrameDesc->bmCapabilities & 0x01) {
		if (gDoAnnotation) { AppendTextBuffer("  -> Still Images are supported\r\n"); }
		else { AppendTextBuffer("\r\n"); }
	}
	else if (VendorVidFrameDesc->bmCapabilities & 0xFF)
	{
		//@@TestCase B29.3
		//@@WARNING
		//@@Descriptor Field - bmCapabilities
		//@@bmCapabilities has a bit using reserved areas that should be set to zero
		AppendTextBuffer("\r\n*!*WARNING:  bmCapabilities is using reserved areas.\r\n");
		OOPS();
	}
	else { AppendTextBuffer("\r\n"); }
	AppendTextBuffer("wWidth:                          0x%04X = %d\r\n", VendorVidFrameDesc->wWidth, VendorVidFrameDesc->wWidth);
	AppendTextBuffer("wHeight:                         0x%04X = %d\r\n", VendorVidFrameDesc->wHeight, VendorVidFrameDesc->wHeight);
	AppendTextBuffer("dwMinBitRate:                0x%08X\r\n", VendorVidFrameDesc->dwMinBitRate);
	AppendTextBuffer("dwMaxBitRate:                0x%08X\r\n", VendorVidFrameDesc->dwMaxBitRate);
	AppendTextBuffer("dwMaxVideoFrameBufferSize:   0x%08X\r\n", VendorVidFrameDesc->dwMaxVideoFrameBufferSize);
	// To convert the default frame interval, which is in 100 ns units,  to  milliseconds, we divide by 10,000.
	// 100 ns = 10^(-7) seconds = 10^(-7) sec * 1000 msec/sec = 10^(-7) * 10^3 milleseconds = 10^(-4) seconds
	// = 1/10,000 milliseconds


	// To convert the frame interval to Hz, we divide by 10,000,000 and then take the inverse

	AppendTextBuffer("dwDefaultFrameInterval:      0x%08X = %lf mSec (%4.2f Hz)\r\n",
		VendorVidFrameDesc->dwDefaultFrameInterval,
		((double)VendorVidFrameDesc->dwDefaultFrameInterval) / 10000.0,
		(10000000.0 / ((double)VendorVidFrameDesc->dwDefaultFrameInterval))
	);
	AppendTextBuffer("bFrameIntervalType:                0x%02X\r\n", VendorVidFrameDesc->bFrameIntervalType);

	if (VendorVidFrameDesc->wWidth == 0)
	{
		//@@TestCase B29.4 (descript.c line 1362)
		//@@ERROR
		//@@Descriptor Field - wWidth
		//@@wWidth is set to zero which is not in accordance with the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  wWidth must be nonzero\r\n");
		OOPS();
	}

	if (VendorVidFrameDesc->wHeight == 0)
	{
		//@@TestCase B29.5 (descript.c line 1367)
		//@@ERROR
		//@@Descriptor Field - wHeight
		//@@wHeight is set to zero which is not in accordance with the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  wHeight must be nonzero\r\n");
		OOPS();
	}

	if (VendorVidFrameDesc->dwMinBitRate == 0)
	{
		//@@TestCase B29.6 (descript.c line 1372)
		//@@ERROR
		//@@Descriptor Field - dwMinBitRate
		//@@dwMinBitRate is set to zero which is not in accordance with the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  dwMinBitRate must be nonzero\r\n");
		OOPS();
	}

	if (VendorVidFrameDesc->dwMaxBitRate == 0)
	{
		//@@TestCase B29.7 (descript.c line 1377)
		//@@ERROR
		//@@Descriptor Field - dwMaxBitRate
		//@@dwMaxBitRate is set to zero which is not in accordance with the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  dwMaxBitRate must be nonzero\r\n");
		OOPS();
	}

	if (VendorVidFrameDesc->dwMinBitRate > VendorVidFrameDesc->dwMaxBitRate)
	{
		//@@TestCase B29.8
		//@@ERROR
		//@@Descriptor Field - dwMinBitRate and dwMaxBitRate
		//@@Verify that dwMaxBitRate is greater than dwMinBitRate
		AppendTextBuffer("*!*ERROR:  dwMinBitRate should be less than dwMaxBitRate\r\n");
		OOPS();
	}
	else
	{
		if (VendorVidFrameDesc->bFrameIntervalType == 1 &&
			VendorVidFrameDesc->dwMinBitRate != VendorVidFrameDesc->dwMaxBitRate)
		{
			//@@TestCase B29.9
			//@@WARNING
			//@@Descriptor Field - bFrameIntervalType, dwMinBitRate, and dwMaxBitRate
			//@@Verify that dwMaxBitRate is equal to dwMinBitRate if bFrameIntervalType is 1
			AppendTextBuffer("*!*WARNING:  if bFrameIntervalType is 1 then dwMinBitRate "\
				"should equal dwMaxBitRate\r\n");
			OOPS();
		}
	}

	if (VendorVidFrameDesc->dwMaxVideoFrameBufferSize == 0)
	{
		//@@TestCase B29.10 (descript.c line 1382)
		//@@WARNING
		//@@Descriptor Field - dwMaxVideoFrameBufferSize
		//@@dwMaxVideoFrameBufferSize is set to zero which is not in accordance with the USB Video Device Specification
		AppendTextBuffer("*!*WARNING:  dwMaxVideoFrameBufferSize must be nonzero\r\n");
		OOPS();
	}
	if (VendorVidFrameDesc->dwDefaultFrameInterval == 0)
	{
		//@@TestCase B29.11 (descript.c line 1020)
		//@@WARNING
		//@@Descriptor Field - dwDefaultFrameInterval
		//@@dwDefaultFrameInterval must be nonzero
		AppendTextBuffer("*!*WARNING:  dwDefaultFrameInterval must be nonzero\r\n");
		OOPS();
	}

	if (VendorVidFrameDesc->bFrameIntervalType == 0)
	{
		DisplayVendorVidContinuousFrameType(VendorVidFrameDesc);
	}
	else
	{
		DisplayVendorVidDiscreteFrameType(VendorVidFrameDesc);
	}
	// This descriptor is deprecated for UVC 1.1
#ifdef H264_SUPPORT
	if (UVC10 != g_chUVCversion)
	{
		AppendTextBuffer("*!*ERROR: This format is NOT ALLOWED for UVC  version >= 1.1 devices\r\n");
	}
#else
	if (UVC11 == g_chUVCversion)
	{
		AppendTextBuffer("*!*ERROR: This format is NOT ALLOWED for UVC 1.1 devices\r\n");
	}
#endif
	return TRUE;
}


//*****************************************************************************
//
// DisplayVendorVidContinuousFrameType()
//
//*****************************************************************************

BOOL
DisplayVendorVidContinuousFrameType(
	PVIDEO_FRAME_VENDOR VContinuousDesc
)
{
	//@@DisplayVendorVidContinuousFrameType -Vendor Video Continuous Frame
	ULONG dwMinFrameInterval = VContinuousDesc->adwFrameInterval[0];
	ULONG dwMaxFrameInterval = VContinuousDesc->adwFrameInterval[1];
	ULONG dwFrameIntervalStep = VContinuousDesc->adwFrameInterval[2];

	AppendTextBuffer("===>Additional Continuous Frame Type Data\r\n");
	// To convert the default frame interval, which is in 100 ns units,  to  milliseconds, we divide by 10,000.
	// 100 ns = 10^(-7) seconds = 10^(-7) sec * 1000 msec/sec = 10^(-7) * 10^3 milleseconds = 10^(-4) seconds
	// = 1/10,000 milliseconds


	// To convert the frame interval to Hz, we divide by 10,000,000 and then take the inverse


	AppendTextBuffer("dwMinFrameInterval:          0x%08X = %lf mSec (%d Hz)\r\n",
		dwMinFrameInterval,
		((double)dwMinFrameInterval) / 10000.0,
		(ULONG)(10000000.0 / ((double)dwMinFrameInterval) + 0.5));

	AppendTextBuffer("dwMaxFrameInterval:          0x%08X = %lf mSec (%d Hz)\r\n",
		dwMaxFrameInterval,
		((double)dwMaxFrameInterval) / 10000.0,
		(ULONG)(10000000.0 / ((double)dwMaxFrameInterval) + 0.5));
	AppendTextBuffer("dwFrameIntervalStep:         0x%08X\r\n", dwFrameIntervalStep);

	if (dwMinFrameInterval == 0)
	{
		//@@TestCase B30.2  (descript.c line 1388)
		//@@ERROR
		//@@Descriptor Field - dwMinFrameInterval
		//@@dwMinFrameInterval is set to zero which is not in accordance with the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  dwMinFrameInterval = 0, this invalidates the descriptor\r\n");
		OOPS();
	}

	if (dwMaxFrameInterval == 0)
	{
		//@@TestCase B30.3 (descript.c line 1388)
		//@@ERROR
		//@@Descriptor Field - dwMaxFrameInterval
		//@@dwMaxFrameInterval is set to zero which is not in accordance with the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  dwMaxFrameInterval = 0, this invalidates the descriptor\r\n");
		OOPS();
	}

	if (dwMinFrameInterval > dwMaxFrameInterval)
	{
		//@@TestCase B30.4  (descript.c line 1405)
		//@@ERROR
		//@@Descriptor Field - dwMinFrameInterval and dwMaxFrameInterval
		//@@Verify that dwMaxFrameInterval is greater than dwMinFrameInterval
		AppendTextBuffer("*!*ERROR:  dwMinFrameInterval is larger that dwMaxFrameInterval, this invalidates the descriptor\r\n");
		OOPS();
	}
	else if ((dwMinFrameInterval + dwFrameIntervalStep) > dwMaxFrameInterval)
	{
		//@@TestCase B30.5
		//@@WARNING
		//@@Descriptor Field - dwFrameIntervalStep, dwMinFrameInterval, and dwMaxFrameInterval
		//@@Verify that dwMaxFrameInterval is greater than dwMinFrameInterval combined with dwFrameIntervalStep
		AppendTextBuffer("*!*WARNING:  dwMinFrameInterval + dwFrameIntervalStep is greater than dwMaxFrameInterval, this could cause problems\r\n");
		OOPS();
	}
	else if ((dwMaxFrameInterval - dwMinFrameInterval) == 0)
	{
		//@@TestCase B30.6
		//@@CAUTION
		//@@Descriptor Field - dwFrameIntervalStep
		//@@Suggestion to use descrite frames if dwFrameIntervalStep is zero
		AppendTextBuffer("*!*CAUTION:  dwFrameIntervalStep equals zero, consider using discrete frames\r\n");
		OOPS();
	}
	else if ((dwMaxFrameInterval - dwMinFrameInterval) % dwFrameIntervalStep)
	{
		//@@TestCase B30.7  (descript.c line 1414)
		//@@ERROR
		//@@Descriptor Field - dwFrameIntervalStep, dwMinFrameInterval, and dwMaxFrameInterval
		//@@Verify that the difference between dwMaxFrameInterval and dwMinFrameInterval is evenly divisible by dwFrameIntervalStep
		AppendTextBuffer("*!*ERROR:  dwMaxFrameInterval minus dwMinFrameInterval  is not evenly divisible by dwFrameIntervalStep, this could cause problems\r\n");
		OOPS();
	}

	if (dwFrameIntervalStep == 0 && (dwMaxFrameInterval - dwMinFrameInterval))
	{
		//@@TestCase B30.8  (descript.c line 1394)
		//@@ERROR
		//@@Descriptor Field - dwFrameIntervalStep, dwMinFrameInterval, and dwMaxFrameInterval
		//@@Verify that the dwFrameIntervalStep is not zero if there is a difference between dwMaxFrameInterval and dwMinFrameInterval
		AppendTextBuffer("*!*ERROR:  dwFrameIntervalStep = 0, this invalidates the descriptor when there is a difference between \r\n          dwMinFrameInterval and dwMaxFrameInterval\r\n");
		OOPS();
	}

	return TRUE;
}


//*****************************************************************************
//
// DisplayVendorVidDiscreteFrameType()
//
//*****************************************************************************

BOOL
DisplayVendorVidDiscreteFrameType(
	PVIDEO_FRAME_VENDOR VDiscreteDesc
)
{
	//@@DisplayVendorVidDiscreteFrameType -Vendor Video Discrete Frame
	UINT    iNdex = 1;
	UINT    iCurFrame = 0;
	ULONG* ulFrameInterval = NULL;

	AppendTextBuffer("===>Additional Discrete Frame TypeData\r\n");

	// There are (VDiscreteDesc->bFrameIntervalType) dwFrameIntervals
	for (; iNdex <= VDiscreteDesc->bFrameIntervalType; iNdex++, iCurFrame++)
	{
		ulFrameInterval = &VDiscreteDesc->adwFrameInterval[iCurFrame];
		// To convert the default frame interval, which is in 100 ns units,  to  milliseconds, we divide by 10,000.
		// 100 ns = 10^(-7) seconds = 10^(-7) sec * 1000 msec/sec = 10^(-7) * 10^3 milleseconds = 10^(-4) seconds
		// = 1/10,000 milliseconds


		// To convert the frame interval to Hz, we divide by 10,000,000 and then take the inverse
		AppendTextBuffer("dwFrameInterval[%d]:          0x%08X = %lf mSec (%4.2f Hz)\r\n",
			iNdex, *ulFrameInterval,
			((double)*ulFrameInterval) / 10000.0,
			(10000000.0 / ((double)*ulFrameInterval))
		);
		if (0 == *ulFrameInterval)
		{
			//@@TestCase B31.1 (descript.c line 1061)
			//@@ERROR
			//@@Descriptor Field - dwFrameInterval[x]
			//@@dwFrameInterval[x] must be non-zero
			AppendTextBuffer("*!*ERROR:  dwFrameInterval[%d] must be non-zero\r\n", iNdex);
			OOPS();
		}
		if ((iNdex > 1) && (*ulFrameInterval <= VDiscreteDesc->adwFrameInterval[iCurFrame - 1]))
		{
			//@@TestCase B31.2 (descript.c line 1067)
			//@@ERROR
			//@@Descriptor Field - dwFrameInterval[x]
			//@@dwFrameInterval[n] must be greater than dwFrameInterval[n - 1]
			AppendTextBuffer("*!*ERROR:  dwFrameInterval[0x%02X] must be "\
				"greater than preceding dwFrameInterval[0x%02X]\r\n", iNdex, iNdex - 1);
			OOPS();
		}
	}

	return TRUE;
}

//*****************************************************************************
//
// DisplayFramePayloadFormat() 
//
//*****************************************************************************

BOOL
DisplayFramePayloadFormat(
	PVIDEO_FORMAT_FRAME FramePayloadFormatDesc
)
{
	//@@DisplayFramePayloadFormat - FrameBased Payload Format
	PCHAR pStr = NULL;
	OLECHAR szGUID[256];
	int i = 0;

	// Initialize the default Frame
	g_chFrameBasedFrameDefault = FramePayloadFormatDesc->bDefaultFrameIndex;

	memset((LPOLESTR)szGUID, 0, sizeof(OLECHAR) * 256);
	i = StringFromGUID2((REFGUID)&FramePayloadFormatDesc->guidFormat, (LPOLESTR)szGUID, 255);
	i++;

	AppendTextBuffer("\r\n          ===>Video Streaming Frame Based Payload Format Type Descriptor<===\r\n");
	AppendTextBuffer("bLength:                           0x%02X\r\n", FramePayloadFormatDesc->bLength);
	AppendTextBuffer("bDescriptorType:                   0x%02X\r\n", FramePayloadFormatDesc->bDescriptorType);
	AppendTextBuffer("bDescriptorSubtype:                0x%02X\r\n", FramePayloadFormatDesc->bDescriptorSubtype);
	AppendTextBuffer("bFormatIndex:                      0x%02X\r\n", FramePayloadFormatDesc->bFormatIndex);
	AppendTextBuffer("bNumFrameDescriptors:              0x%02X\r\n", FramePayloadFormatDesc->bNumFrameDescriptors);
	AppendTextBuffer("guidFormat:                        %S", szGUID);

	pStr = VidFormatGUIDCodeToName((REFGUID)&FramePayloadFormatDesc->guidFormat);
	if (pStr)
	{
		if (gDoAnnotation)
		{
			AppendTextBuffer(" = %s Format", pStr);
		}
	}
	AppendTextBuffer("\r\n");
	AppendTextBuffer("bBitsPerPixel:                     0x%02X\r\n", FramePayloadFormatDesc->bBitsPerPixel);
	AppendTextBuffer("bDefaultFrameIndex:                0x%02X\r\n", FramePayloadFormatDesc->bDefaultFrameIndex);

	if (FramePayloadFormatDesc->bLength != sizeof(VIDEO_FORMAT_FRAME))
	{
		//@@ERROR
		//@@Descriptor Field - bLength
		//@@The declared length in the device descriptor is not equal to the required 
		//@@length in the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  bLength of %d incorrect, should be %d\r\n",
			FramePayloadFormatDesc->bLength,
			sizeof(VIDEO_FORMAT_FRAME));
		OOPS();
	}

	if (FramePayloadFormatDesc->bFormatIndex == 0)
	{
		//@@ERROR
		//@@Descriptor Field - bFormatIndex
		//@@bFormatIndex is set to zero which is not in accordance with the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  bFormatIndex = 0, this is a 1 based index\r\n");
		OOPS();
	}

	if (FramePayloadFormatDesc->bNumFrameDescriptors == 0)
	{
		//@@ERROR
		//@@Descriptor Field - bNumFrameDescriptors
		//@@bNumFrameDescriptors is set to zero which is not in accordance with the 
		//@@USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  bNumFrameDescriptors = 0, must have at least 1 Frame descriptor\r\n");
		OOPS();
	}

	if (!(pStr))
	{
		//@@WARNING
		//@@Descriptor Field - guidFormat
		//@@guidFormat is set to unknown or undefined format
		AppendTextBuffer("\r\n*!*WARNING:  guidFormat is an unknown format\r\n");
		OOPS();
	}

	if (FramePayloadFormatDesc->bBitsPerPixel == 0)
	{
		//@@ERROR
		//@@Descriptor Field - bBitsPerPixel
		//@@bBitsPerPixel is set to zero which is not in accordance with the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  bBitsPerPixel = 0, this invalidates the descriptor\r\n");
		OOPS();
	}

	if (FramePayloadFormatDesc->bDefaultFrameIndex == 0 || FramePayloadFormatDesc->bDefaultFrameIndex >
		FramePayloadFormatDesc->bNumFrameDescriptors)
	{
		//@@ERROR
		//@@Descriptor Field - bDefaultFrameIndex
		//@@The value for bDefaultFrameIndex is not greater than 0 or less than or equal to bNumFrameDescriptors
		AppendTextBuffer("*!*ERROR:  The value %d for the bDefaultFrameIndex is out of range, this invalidates the descriptor\r\n*!*The proper range is 1 to %d)",
			FramePayloadFormatDesc->bDefaultFrameIndex,
			FramePayloadFormatDesc->bNumFrameDescriptors);
		OOPS();
	}

	AppendTextBuffer("bAspectRatioX:                     0x%02X\r\n",
		FramePayloadFormatDesc->bAspectRatioX);
	AppendTextBuffer("bAspectRatioY:                     0x%02X",
		FramePayloadFormatDesc->bAspectRatioY);

	if (((FramePayloadFormatDesc->bmInterlaceFlags & 0x01) &&
		(FramePayloadFormatDesc->bAspectRatioY != 0 &&
			FramePayloadFormatDesc->bAspectRatioX != 0)))
	{
		if (gDoAnnotation)
		{
			AppendTextBuffer("  -> Aspect Ratio is set for a %d:%d display",
				(FramePayloadFormatDesc->bAspectRatioX), (FramePayloadFormatDesc->bAspectRatioY));
		}
		else
		{
			if (FramePayloadFormatDesc->bAspectRatioY != 0 || FramePayloadFormatDesc->bAspectRatioX != 0)
			{
				//@@ERROR
				//@@Descriptor Field - bAspectRatioX, bAspectRatioY
				//@@Verify that that bAspectRatioX and bAspectRatioY are  set to zero 
				//@@  if stream is non-interlaced
				AppendTextBuffer("\r\n*!*ERROR:  Both bAspectRatioX and bAspectRatioY "\
					"must equal 0 if stream is non-interlaced");
				OOPS();
			}
		}
	}
	AppendTextBuffer("\r\nbmInterlaceFlags:                  0x%02X\r\n",
		FramePayloadFormatDesc->bmInterlaceFlags);

	if (gDoAnnotation)
	{
		AppendTextBuffer("     D0    = 0x%02X Interlaced stream or variable: %s\r\n",
			(FramePayloadFormatDesc->bmInterlaceFlags & 1),
			(FramePayloadFormatDesc->bmInterlaceFlags & 1) ? "Yes" : "No");
		AppendTextBuffer("     D1    = 0x%02X Fields per frame: %s\r\n",
			((FramePayloadFormatDesc->bmInterlaceFlags >> 1) & 1),
			((FramePayloadFormatDesc->bmInterlaceFlags >> 1) & 1) ? "1 field" : "2 fields");
		AppendTextBuffer("     D2    = 0x%02X Field 1 first: %s\r\n",
			((FramePayloadFormatDesc->bmInterlaceFlags >> 2) & 1),
			((FramePayloadFormatDesc->bmInterlaceFlags >> 2) & 1) ? "Yes" : "No");
		//@@Descriptor Field - bmInterlaceFlags
		//@@Validate that reserved bits (D3) are set to zero.
		AppendTextBuffer("     D3    = 0x%02X Reserved%s\r\n",
			((FramePayloadFormatDesc->bmInterlaceFlags >> 3) & 1),
			((FramePayloadFormatDesc->bmInterlaceFlags >> 3) & 1) ?
			"\r\n*!*ERROR: Reserved to 0" : "");
		AppendTextBuffer("     D4..5 = 0x%02X Field patterns  ->",
			((FramePayloadFormatDesc->bmInterlaceFlags >> 4) & 3));
		switch (FramePayloadFormatDesc->bmInterlaceFlags & 0x30)
		{
		case 0x00:
			AppendTextBuffer(" Field 1 only");
			break;
		case 0x10:
			AppendTextBuffer(" Field 2 only");
			break;
		case 0x20:
			AppendTextBuffer(" Regular Pattern of fields 1 and 2");
			break;
		case 0x30:
			AppendTextBuffer(" Random Pattern of fields 1 and 2");
			break;
		}
		AppendTextBuffer("\r\n     D6..7 = 0x%02X Display Mode  ->",
			((FramePayloadFormatDesc->bmInterlaceFlags >> 6) & 3));

		switch (FramePayloadFormatDesc->bmInterlaceFlags & 0xC0)
		{
		case 0x00:
			AppendTextBuffer(" Bob only");
			break;
		case 0x40:
			AppendTextBuffer(" Weave only");
			break;
		case 0x80:
			AppendTextBuffer(" Bob or weave");
			break;
		case 0xC0:
			//@@Descriptor Field - bmInterlaceFlags
			//@@Question - Should we validate that reserved bits are set to zero?
			AppendTextBuffer(" Reserved");
			break;
		}
	}

	//@@Descriptor Field - bCopyProtect
	//@@Question - Are their reserved bits and should we validate that 
	//@@  reserved bits are set to zero?
	AppendTextBuffer("\r\nbCopyProtect:                      0x%02X",
		FramePayloadFormatDesc->bCopyProtect);
	if (gDoAnnotation)
	{
		if (FramePayloadFormatDesc->bCopyProtect)
			AppendTextBuffer("  -> Duplication Restricted");
		else
			AppendTextBuffer("  -> Duplication Unrestricted");
	}

	//@@Descriptor Field - bVariableSize
	AppendTextBuffer("\r\nbVariableSize:                     0x%02X",
		FramePayloadFormatDesc->bVariableSize);
	if (gDoAnnotation)
	{
		if (FramePayloadFormatDesc->bVariableSize)
			AppendTextBuffer("  -> Variable Size");
		else
			AppendTextBuffer("  -> Fixed Size");
	}
	AppendTextBuffer("\r\n");

	// Check that the correct number of Frame Descriptors and one Color Matching
	//  descriptor follow
	CheckForColorMatchingDesc((PVIDEO_SPECIFIC)FramePayloadFormatDesc,
		FramePayloadFormatDesc->bNumFrameDescriptors, VS_FRAME_FRAME_BASED);

	// This descriptor is new for UVC 1.1
	if (UVC10 == g_chUVCversion)
	{
		AppendTextBuffer("*!*ERROR: This format is NOT ALLOWED for UVC 1.0 devices\r\n");
	}
	return TRUE;
}


//*****************************************************************************
//
// DisplayFramePayloadFrame()
//
//*****************************************************************************

BOOL
DisplayFramePayloadFrame(
	PVIDEO_FRAME_FRAME FramePayloadFrameDesc
)
{
	size_t bLength = 0;
	bLength = SizeOfVideoFrameFrame(FramePayloadFrameDesc);

	//@@DisplayFramePayloadFrame -Frame Based Payload Frame

	AppendTextBuffer("\r\n          ===>Video Streaming Frame Based Payload Frame Type Descriptor<===\r\n");
	if (gDoAnnotation)
	{
		if (FramePayloadFrameDesc->bFrameIndex == g_chFrameBasedFrameDefault)
		{
			AppendTextBuffer("          --->This is the Default (optimum) Frame index\r\n");
		}
	}
	AppendTextBuffer("bLength:                           0x%02X\r\n", FramePayloadFrameDesc->bLength);
	AppendTextBuffer("bDescriptorType:                   0x%02X\r\n", FramePayloadFrameDesc->bDescriptorType);
	AppendTextBuffer("bDescriptorSubtype:                0x%02X\r\n", FramePayloadFrameDesc->bDescriptorSubtype);
	AppendTextBuffer("bFrameIndex:                       0x%02X\r\n", FramePayloadFrameDesc->bFrameIndex);
	AppendTextBuffer("bmCapabilities:                    0x%02X\r\n", FramePayloadFrameDesc->bmCapabilities);
	AppendTextBuffer("wWidth:                          0x%04X = %d\r\n", FramePayloadFrameDesc->wWidth, FramePayloadFrameDesc->wWidth);
	AppendTextBuffer("wHeight:                         0x%04X = %d\r\n", FramePayloadFrameDesc->wHeight, FramePayloadFrameDesc->wHeight);
	AppendTextBuffer("dwMinBitRate:                0x%08X\r\n", FramePayloadFrameDesc->dwMinBitRate);
	AppendTextBuffer("dwMaxBitRate:                0x%08X\r\n", FramePayloadFrameDesc->dwMaxBitRate);
	// To convert the default frame interval, which is in 100 ns units,  to  milliseconds, we divide by 10,000.
	// 100 ns = 10^(-7) seconds = 10^(-7) sec * 1000 msec/sec = 10^(-7) * 10^3 milleseconds = 10^(-4) seconds
	// = 1/10,000 milliseconds


	// To convert the frame interval to Hz, we divide by 10,000,000 and then take the inverse

	AppendTextBuffer("dwDefaultFrameInterval:      0x%08X = %lf mSec (%4.2f Hz)\r\n",
		FramePayloadFrameDesc->dwDefaultFrameInterval,
		((double)FramePayloadFrameDesc->dwDefaultFrameInterval) / 10000.0,
		(10000000.0 / ((double)FramePayloadFrameDesc->dwDefaultFrameInterval))
	);
	AppendTextBuffer("bFrameIntervalType:                0x%02X\r\n", FramePayloadFrameDesc->bFrameIntervalType);

	if (FramePayloadFrameDesc->bLength != bLength)
	{
		//@@ERROR
		//@@Descriptor Field - bLength
		//@@The declared length in the device descriptor is not equal to the required 
		//@@length in the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  bLength of %d incorrect, should be %d\r\n",
			FramePayloadFrameDesc->bLength, bLength);
		OOPS();
	}

	if (FramePayloadFrameDesc->bFrameIndex == 0)
	{
		//@@ERROR
		//@@Descriptor Field - bFrameIndex
		//@@bFrameIndex must be nonzero 
		AppendTextBuffer("*!*ERROR:  bFrameIndex = 0, this is a 1 based index\r\n");
		OOPS();
	}

	//@@Descriptor Field - bmCapabilities
	//@@Question:  Should we try to verify that bmCapabilities is valid?
	//    AppendTextBuffer("bmCapabilities:                    0x%02X\r\n", UnCompFrameDesc->bmCapabilities);

	if (FramePayloadFrameDesc->wWidth == 0)
	{
		//@@ERROR
		//@@Descriptor Field - wWidth
		//@@wWidth must be nonzero
		AppendTextBuffer("*!*ERROR:  wWidth must be nonzero\r\n");
		OOPS();
	}

	if (FramePayloadFrameDesc->wHeight == 0)
	{
		//@@ERROR
		//@@Descriptor Field - wHeight
		//@@wHeight must be nonzero
		AppendTextBuffer("*!*ERROR:  wHeight must be nonzero\r\n");
		OOPS();
	}

	if (FramePayloadFrameDesc->dwMinBitRate == 0)
	{
		//@@ERROR
		//@@Descriptor Field - dwMinBitRate
		//@@dwMinBitRate must be nonzero
		AppendTextBuffer("*!*ERROR:  dwMinBitRate must be nonzero\r\n");
		OOPS();
	}

	if (FramePayloadFrameDesc->dwMaxBitRate == 0)
	{
		//@@ERROR
		//@@Descriptor Field - dwMaxBitRate
		//@@dwMaxBitRate must be nonzero
		AppendTextBuffer("*!*ERROR:  dwMaxBitRate must be nonzero\r\n");
		OOPS();
	}

	if (FramePayloadFrameDesc->dwMinBitRate > FramePayloadFrameDesc->dwMaxBitRate)
	{
		//@@ERROR
		//@@Descriptor Field - dwMinBitRate and dwMaxBitRate
		//@@Verify that dwMaxBitRate is greater than dwMinBitRate
		AppendTextBuffer("*!*ERROR:  dwMinBitRate should be less than dwMaxBitRate\r\n");
		OOPS();
	}
	else
	{
		if (FramePayloadFrameDesc->bFrameIntervalType == 1 &&
			FramePayloadFrameDesc->dwMinBitRate != FramePayloadFrameDesc->dwMaxBitRate)
		{
			//@@WARNING
			//@@Descriptor Field - bFrameIntervalType, dwMinBitRate, and dwMaxBitRate
			//@@Verify that dwMaxBitRate is equal to dwMinBitRate if bFrameIntervalType is 1
			AppendTextBuffer("*!*WARNING:  if bFrameIntervalType is 1 then dwMinBitRate "\
				"should equal dwMaxBitRate\r\n");
			OOPS();
		}
	}

	if (FramePayloadFrameDesc->dwDefaultFrameInterval == 0)
	{
		//@@TestCase B16.11 (descript.c line 1020)
		//@@WARNING
		//@@Descriptor Field - dwDefaultFrameInterval
		//@@dwDefaultFrameInterval must be nonzero
		AppendTextBuffer("*!*WARNING:  dwDefaultFrameInterval must be nonzero\r\n");
		OOPS();
	}

	if (0 == FramePayloadFrameDesc->bFrameIntervalType)
	{
		DisplayFramePayloadContinuousFrameType(FramePayloadFrameDesc);
	}
	else
	{
		DisplayFramePayloadDiscreteFrameType(FramePayloadFrameDesc);
	}
	// This descriptor is new for UVC 1.1
	if (UVC10 == g_chUVCversion)
	{
		AppendTextBuffer("*!*ERROR: This format is NOT ALLOWED for UVC 1.0 devices\r\n");
	}
	return TRUE;
}

//*****************************************************************************
//
// DisplayFramePayloadContinuousFrameType()
//
//*****************************************************************************

BOOL
DisplayFramePayloadContinuousFrameType(
	PVIDEO_FRAME_FRAME FContinuousDesc
)
{
	//@@DisplayFramePayloadContinuousFrameType -Frame Payload Continuous Frame
	ULONG dwMinFrameInterval = FContinuousDesc->adwFrameInterval[0];
	ULONG dwMaxFrameInterval = FContinuousDesc->adwFrameInterval[1];
	ULONG dwFrameIntervalStep = FContinuousDesc->adwFrameInterval[2];

	AppendTextBuffer("===>Additional Continuous Frame Type Data\r\n");
	// To convert the default frame interval, which is in 100 ns units,  to  milliseconds, we divide by 10,000.
	// 100 ns = 10^(-7) seconds = 10^(-7) sec * 1000 msec/sec = 10^(-7) * 10^3 milleseconds = 10^(-4) seconds
	// = 1/10,000 milliseconds


	// To convert the frame interval to Hz, we divide by 10,000,000 and then take the inverse


	AppendTextBuffer("dwMinFrameInterval:          0x%08X = %lf mSec (%d Hz)\r\n",
		dwMinFrameInterval,
		((double)dwMinFrameInterval) / 10000.0,
		(ULONG)(10000000.0 / ((double)dwMinFrameInterval) + 0.5));

	AppendTextBuffer("dwMaxFrameInterval:          0x%08X = %lf mSec (%d Hz)\r\n",
		dwMaxFrameInterval,
		((double)dwMaxFrameInterval) / 10000.0,
		(ULONG)(10000000.0 / ((double)dwMaxFrameInterval) + 0.5));

	AppendTextBuffer("dwFrameIntervalStep:         0x%08X\r\n", dwFrameIntervalStep);

	if (dwMinFrameInterval == 0)
	{
		//@@ERROR
		//@@Descriptor Field - dwMinFrameInterval
		//@@dwMinFrameInterval is set to zero which is not in accordance with the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  dwMinFrameInterval = 0, this invalidates the descriptor\r\n");
		OOPS();
	}

	if (dwMaxFrameInterval == 0)
	{
		//@@ERROR
		//@@Descriptor Field - dwMaxFrameInterval
		//@@dwMaxFrameInterval is set to zero which is not in accordance with the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  dwMaxFrameInterval = 0, this invalidates the descriptor\r\n");
		OOPS();
	}

	if (dwMinFrameInterval > dwMaxFrameInterval)
	{
		//@@ERROR
		//@@Descriptor Field - dwMinFrameInterval and dwMaxFrameInterval
		//@@Verify that dwMaxFrameInterval is greater than dwMinFrameInterval
		AppendTextBuffer("*!*ERROR:  dwMinFrameInterval is larger that dwMaxFrameInterval, this invalidates the descriptor\r\n");
		OOPS();
	}
	else if ((dwMinFrameInterval + dwFrameIntervalStep) > dwMaxFrameInterval)
	{
		//@@WARNING
		//@@Descriptor Field - dwFrameIntervalStep, dwMinFrameInterval, and dwMaxFrameInterval
		//@@Verify that dwMaxFrameInterval is greater than dwMinFrameInterval combined with dwFrameIntervalStep
		AppendTextBuffer("*!*WARNING:  dwMinFrameInterval + dwFrameIntervalStep is greater than dwMaxFrameInterval, this could cause problems\r\n");
		OOPS();
	}
	else if ((dwMaxFrameInterval - dwMinFrameInterval) == 0)
	{
		//@@CAUTION
		//@@Descriptor Field - dwFrameIntervalStep
		//@@Suggestion to use descrite frames if dwFrameIntervalStep is zero
		AppendTextBuffer("*!*CAUTION:  dwFrameIntervalStep equals zero, consider using discrete frames\r\n");
		OOPS();
	}
	else if ((dwMaxFrameInterval - dwMinFrameInterval) % dwFrameIntervalStep)
	{
		//@@WARNING
		//@@Descriptor Field - dwFrameIntervalStep, dwMinFrameInterval, and dwMaxFrameInterval
		//@@Verify that the difference between dwMaxFrameInterval and dwMinFrameInterval is evenly divisible by dwFrameIntervalStep
		AppendTextBuffer("*!*WARNING:  dwMaxFrameInterval minus dwMinFrameInterval  is not evenly divisible by dwFrameIntervalStep, this could cause problems\r\n");
		OOPS();
	}

	if (dwFrameIntervalStep == 0 && (dwMaxFrameInterval - dwMinFrameInterval))
	{
		//@@WARNING
		//@@Descriptor Field - dwFrameIntervalStep, dwMinFrameInterval, and dwMaxFrameInterval
		//@@Verify that the dwFrameIntervalStep is not zero if there is a difference between dwMaxFrameInterval and dwMinFrameInterval
		AppendTextBuffer("*!*WARNING:  dwFrameIntervalStep = 0, this invalidates the descriptor when there is a difference between dwMinFrameInterval and dwMaxFrameInterval\r\n");
		OOPS();
	}

	return TRUE;
}

//*****************************************************************************
//
// DisplayFramePayloadDiscreteFrameType()
//
//*****************************************************************************

BOOL
DisplayFramePayloadDiscreteFrameType(
	PVIDEO_FRAME_FRAME FDiscreteDesc
)
{
	//@@DisplayFramePayloadDiscreteFrameType -Frame Based Payload Discrete Frame
	UINT    iNdex = 1;
	UINT    iCurFrame = 0;
	ULONG* ulFrameInterval = NULL;

	AppendTextBuffer("===>Additional Discrete Frame Type Data\r\n");

	// There are (UDiscreteDesc->bFrameIntervalType) dwFrameIntervals (1 based index)
	for (; iNdex <= FDiscreteDesc->bFrameIntervalType; iNdex++, iCurFrame++)
	{
		ulFrameInterval = &FDiscreteDesc->adwFrameInterval[iCurFrame];
		// To convert the default frame interval, which is in 100 ns units,  to  milliseconds, we divide by 10,000.
		// 100 ns = 10^(-7) seconds = 10^(-7) sec * 1000 msec/sec = 10^(-7) * 10^3 milleseconds = 10^(-4) seconds
		// = 1/10,000 milliseconds


		// To convert the frame interval to Hz, we divide by 10,000,000 and then take the inverse
		AppendTextBuffer("dwFrameInterval[%d]:          0x%08X = %lf mSec (%4.2f Hz)\r\n",
			iNdex, *ulFrameInterval,
			((double)*ulFrameInterval) / 10000.0,
			(10000000.0 / ((double)*ulFrameInterval))
		);
		if (0 == *ulFrameInterval)
		{
			//@@TestCase B18.1 (descript.c line 1061)
			//@@ERROR
			//@@Descriptor Field - dwFrameInterval[x]
			//@@dwFrameInterval[x] must be non-zero
			AppendTextBuffer("*!*ERROR:  dwFrameInterval[%d] must be non-zero\r\n", iNdex);
			OOPS();
		}
		if ((iNdex > 1) && (*ulFrameInterval <= FDiscreteDesc->adwFrameInterval[iCurFrame - 1]))
		{
			//@@TestCase B18.2 (descript.c line 1067)
			//@@ERROR
			//@@Descriptor Field - dwFrameInterval[x]
			//@@dwFrameInterval[n] must be greater than dwFrameInterval[n - 1]
			AppendTextBuffer("*!*ERROR:  dwFrameInterval[0x%02X] must be "\
				"greater than preceding dwFrameInterval[0x%02X]\r\n", iNdex, iNdex - 1);
			OOPS();
		}
	}
	return TRUE;
}

//*****************************************************************************
//
// DisplayVSEndpoint()
//
//*****************************************************************************

BOOL
DisplayVSEndpoint(
	PVIDEO_CS_INTERRUPT VidEndpointDesc
)
{
	//@@DisplayVSEndpoint - Video Streaming Endpoint
	AppendTextBuffer("\r\n          ===>Class-specific VC Interrupt Endpoint Descriptor<===\r\n");
	AppendTextBuffer("bLength:                           0x%02X \r\n", VidEndpointDesc->bLength);
	AppendTextBuffer("bDescriptorType:                   0x%02X\r\n", VidEndpointDesc->bDescriptorType);
	AppendTextBuffer("bDescriptorSubtype:                0x%02X\r\n", VidEndpointDesc->bDescriptorSubtype);
	AppendTextBuffer("wMaxTransferSize:                0x%04X", VidEndpointDesc->wMaxTransferSize);
	if (gDoAnnotation) {
		AppendTextBuffer(" = (%d) Bytes\r\n", VidEndpointDesc->wMaxTransferSize);
	}
	else { AppendTextBuffer("\r\n"); }

	if (VidEndpointDesc->bLength != sizeof(VIDEO_CS_INTERRUPT))
	{
		//@@TestCase B32.1 (descript.c line 1616)
		//@@ERROR
		//@@Descriptor Field - bLength
		//@@The declared length in the device descriptor is not equal to the required length in the USB Video Device Specification
		AppendTextBuffer("*!*ERROR:  bLength of %d incorrect, should be %d.  USBView cannot correctly display descriptor\r\n",
			VidEndpointDesc->bLength,
			sizeof(VIDEO_CS_INTERRUPT));
		OOPS();
	}

	return TRUE;
}

//*****************************************************************************
//
// VDisplayBytes()
//
//*****************************************************************************

VOID
VDisplayBytes(
	PUCHAR Data,
	USHORT Len
)
{
	USHORT i = 0;

	for (i = 0; i < Len; i++)
	{
		AppendTextBuffer("0x%02X ", Data[i]);

		if (i % 16 == 15)
		{
			AppendTextBuffer("\r\n");
		}
	}

	if (i % 16 != 0)
	{
		AppendTextBuffer("\r\n");
	}
}

//*****************************************************************************
//
// VidFormatGUIDCodeToName()
//
//*****************************************************************************


PCHAR
VidFormatGUIDCodeToName(
	REFGUID VidFormatGUIDCode
)
{
	//  GUID pYUY2 = YUY2_Format;
	//  GUID pNV12 = NV12_Format;
	if (IsEqualGUID(VidFormatGUIDCode, (REFGUID)&YUY2_Format))
	{
		return (PCHAR) & "YUY2";
	}
	if (IsEqualGUID(VidFormatGUIDCode, (REFGUID)&NV12_Format))
	{
		return (PCHAR) & "NV12";
	}
#ifdef H264_SUPPORT
	//  GUID pH264 = H264_Format;
	if (IsEqualGUID(VidFormatGUIDCode, (REFGUID)&H264_Format))
	{
		return (PCHAR) & "H.264";
	}
#endif

	return FALSE;
}

/*****************************************************************************

GetVCInterfaceSize()

*****************************************************************************/

UINT
GetVCInterfaceSize(
	PVIDEO_CONTROL_HEADER_UNIT VCInterfaceDesc
)
{
	PUSB_COMMON_DESCRIPTOR commonDesc = (PUSB_COMMON_DESCRIPTOR)VCInterfaceDesc;
	PUCHAR descEnd = (PUCHAR)VCInterfaceDesc + VCInterfaceDesc->wTotalLength;
	UINT  uCount = 0;

	// return this interface's sum of descriptor lengths
	//   starting from this header until (and not including) the first endpoint
	while ((PUCHAR)commonDesc + sizeof(USB_COMMON_DESCRIPTOR) < descEnd &&
		(PUCHAR)commonDesc + commonDesc->bLength <= descEnd)
	{
		if (commonDesc->bDescriptorType == USB_ENDPOINT_DESCRIPTOR_TYPE)
			break;
		uCount += commonDesc->bLength;
		commonDesc = (PUSB_COMMON_DESCRIPTOR)((PUCHAR)commonDesc + commonDesc->bLength);
	}
	return (uCount);
}

/*****************************************************************************

CheckForColorMatchingDesc ()

Given starting address of format descriptor;
number of frame descriptors;
subtype of frame to look for;

1) walk through each descriptor
= if desc is frame of given subtype, update counter
= if desc is still frame, update counter
= if desc is color matching descriptor, update counter
! if frame is something else, break (all these frames should be consecutive)
! if next frame is beyond ending address of configuration, break

PASS
frame count == numframes passed in
color match == 1
still frames are handled in the video stream input header and the frame displays

*****************************************************************************/

UINT
CheckForColorMatchingDesc(
	PVIDEO_SPECIFIC pFormatDesc,
	UCHAR bNumFrameDescriptors,
	UCHAR bDescriptorSubtype
)
{
	UINT  uFrameCount = 0;
	UINT  uStillFrameCount = 0;
	UINT  uColorCount = 0;

	// DONE if the descriptor address is beyond the configuration range
	for (; ValidateDescAddress((PUSB_COMMON_DESCRIPTOR)pFormatDesc); )
	{
		// DONE if it's not an interface desc
		if (CS_INTERFACE != pFormatDesc->bDescriptorType)
		{
			break;
		}
		switch (pFormatDesc->bDescriptorSubtype)
		{
		case VS_STILL_IMAGE_FRAME:
			uStillFrameCount++;
			break;
		case VS_COLORFORMAT:
			uColorCount++;
			break;
		default:
			if (bDescriptorSubtype == pFormatDesc->bDescriptorSubtype)
			{
				uFrameCount++;
			}
			break;
		}
		pFormatDesc = (PVIDEO_SPECIFIC)((PUCHAR)pFormatDesc + pFormatDesc->bLength);
	}
	if (uFrameCount != bNumFrameDescriptors)
	{
		AppendTextBuffer("*!*ERROR:  Found %d frame descriptors (should be %d)\r\n",
			uFrameCount, bNumFrameDescriptors);
	}
	// We already check Still Frames in the Video Info Header and Still Frames displays
	if (0 == uColorCount)
	{
		AppendTextBuffer("*!*ERROR:  no Color Matching Descriptor for this format\r\n");
	}
	return (uColorCount);
}

/*****************************************************************************

GetVSInterfaceSize()

*****************************************************************************/

UINT
GetVSInterfaceSize(
	PUSB_COMMON_DESCRIPTOR VidInHeaderDesc,
	USHORT wTotalLength
)
{
	PUSB_COMMON_DESCRIPTOR commonDesc = (PUSB_COMMON_DESCRIPTOR)VidInHeaderDesc;
	PUCHAR descEnd = (PUCHAR)VidInHeaderDesc + wTotalLength;
	UINT  uCount = 0;

	// return this interface's sum of descriptor lengths
	//   starting from this header until (and not including) the first endpoint
	while ((PUCHAR)commonDesc + sizeof(USB_COMMON_DESCRIPTOR) < descEnd &&
		(PUCHAR)commonDesc + commonDesc->bLength <= descEnd)
	{
		if (commonDesc->bDescriptorType == USB_ENDPOINT_DESCRIPTOR_TYPE)
			break;
		uCount += commonDesc->bLength;
		commonDesc = (PUSB_COMMON_DESCRIPTOR)((PUCHAR)commonDesc + commonDesc->bLength);
	}
	return (uCount);
}

/*****************************************************************************

ValidateTerminalID()

*****************************************************************************/

BOOL
ValidateTerminalID(
	UINT uTerminalID
)
{
	UNREFERENCED_PARAMETER(uTerminalID);
	return (TRUE);
}
